X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..refs/heads/master:/docs/source/Scroller2.html diff --git a/docs/source/Scroller2.html b/docs/source/Scroller2.html index 972abe60..9607d387 100644 --- a/docs/source/Scroller2.html +++ b/docs/source/Scroller2.html @@ -3,8 +3,8 @@
/** - * @class Ext.grid.Scroller - * @extends Ext.Component - * - * Docked in an Ext.grid.Panel, controls virtualized scrolling and synchronization - * across different sections. - * +/** + * @class Ext.layout.container.boxOverflow.Scroller + * @extends Ext.layout.container.boxOverflow.None * @private */ -Ext.define('Ext.grid.Scroller', { - extend: 'Ext.Component', - alias: 'widget.gridscroller', - weight: 110, - cls: Ext.baseCSSPrefix + 'scroller', - focusable: false, - reservedSpace: 0, - - renderTpl: [ - '<div class="' + Ext.baseCSSPrefix + 'scroller-ct" id="{baseId}_ct">', - '<div class="' + Ext.baseCSSPrefix + 'stretcher" id="{baseId}_stretch"></div>', - '</div>' - ], - - initComponent: function() { - var me = this, - dock = me.dock, - cls = Ext.baseCSSPrefix + 'scroller-vertical', - sizeProp = 'width'; - - me.offsets = {bottom: 0}; - me.scrollProp = 'scrollTop'; - me.vertical = true; - - if (dock === 'top' || dock === 'bottom') { - cls = Ext.baseCSSPrefix + 'scroller-horizontal'; - sizeProp = 'height'; - me.scrollProp = 'scrollLeft'; - me.vertical = false; - me.weight += 5; - } - - me[sizeProp] = me.scrollerSize = Ext.getScrollbarSize()[sizeProp]; +Ext.define('Ext.layout.container.boxOverflow.Scroller', { - me.cls += (' ' + cls); + /* Begin Definitions */ - Ext.applyIf(me.renderSelectors, { - stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher', - scrollEl: '.' + Ext.baseCSSPrefix + 'scroller-ct' - }); - me.callParent(); + extend: 'Ext.layout.container.boxOverflow.None', + requires: ['Ext.util.ClickRepeater', 'Ext.Element'], + alternateClassName: 'Ext.layout.boxOverflow.Scroller', + mixins: { + observable: 'Ext.util.Observable' }, + + /* End Definitions */ - initRenderData: function () { - var me = this, - ret = me.callParent(arguments) || {}; + /** + * @cfg {Boolean} animateScroll + * True to animate the scrolling of items within the layout (ignored if enableScroll is false) + */ + animateScroll: false, - ret.baseId = me.id; + /** + * @cfg {Number} scrollIncrement + * The number of pixels to scroll by on scroller click + */ + scrollIncrement: 20, - return ret; - }, + /** + * @cfg {Number} wheelIncrement + * The number of pixels to increment on mouse wheel scrolling. + */ + wheelIncrement: 10, - afterRender: function() { - var me = this; - me.callParent(); - - me.mon(me.scrollEl, 'scroll', me.onElScroll, me); - Ext.cache[me.el.id].skipGarbageCollection = true; - }, + /** + * @cfg {Number} scrollRepeatInterval + * Number of milliseconds between each scroll while a scroller button is held down + */ + scrollRepeatInterval: 60, - onAdded: function(container) { - // Capture the controlling grid Panel so that we can use it even when we are undocked, and don't have an ownerCt - this.ownerGrid = container; - this.callParent(arguments); - }, + /** + * @cfg {Number} scrollDuration + * Number of milliseconds that each scroll animation lasts + */ + scrollDuration: 400, - getSizeCalculation: function() { - var me = this, - owner = me.getPanel(), - width = 1, - height = 1, - view, tbl; - - if (!me.vertical) { - // TODO: Must gravitate to a single region.. - // Horizontal scrolling only scrolls virtualized region - var items = owner.query('tableview'), - center = items[1] || items[0]; - - if (!center) { - return false; - } - // center is not guaranteed to have content, such as when there - // are zero rows in the grid/tree. We read the width from the - // headerCt instead. - width = center.headerCt.getFullWidth(); + /** + * @cfg {String} beforeCtCls + * CSS class added to the beforeCt element. This is the element that holds any special items such as scrollers, + * which must always be present at the leftmost edge of the Container + */ - if (Ext.isIEQuirks) { - width--; - } - } else { - view = owner.down('tableview:not([lockableInjected])'); - if (!view || !view.el) { - return false; - } - tbl = view.el.child('table', true); - if (!tbl) { - return false; - } + /** + * @cfg {String} afterCtCls + * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers, + * which must always be present at the rightmost edge of the Container + */ - // needs to also account for header and scroller (if still in picture) - // should calculate from headerCt. - height = tbl.offsetHeight; - } - if (isNaN(width)) { - width = 1; - } - if (isNaN(height)) { - height = 1; + /** + * @cfg {String} [scrollerCls='x-box-scroller'] + * CSS class added to both scroller elements if enableScroll is used + */ + scrollerCls: Ext.baseCSSPrefix + 'box-scroller', + + /** + * @cfg {String} beforeScrollerCls + * CSS class added to the left scroller element if enableScroll is used + */ + + /** + * @cfg {String} afterScrollerCls + * CSS class added to the right scroller element if enableScroll is used + */ + + constructor: function(layout, config) { + this.layout = layout; + Ext.apply(this, config || {}); + + this.addEvents( + /** + * @event scroll + * @param {Ext.layout.container.boxOverflow.Scroller} scroller The layout scroller + * @param {Number} newPosition The new position of the scroller + * @param {Boolean/Object} animate If animating or not. If true, it will be a animation configuration, else it will be false + */ + 'scroll' + ); + }, + + initCSSClasses: function() { + var me = this, + layout = me.layout; + + if (!me.CSSinitialized) { + me.beforeCtCls = me.beforeCtCls || Ext.baseCSSPrefix + 'box-scroller-' + layout.parallelBefore; + me.afterCtCls = me.afterCtCls || Ext.baseCSSPrefix + 'box-scroller-' + layout.parallelAfter; + me.beforeScrollerCls = me.beforeScrollerCls || Ext.baseCSSPrefix + layout.owner.getXType() + '-scroll-' + layout.parallelBefore; + me.afterScrollerCls = me.afterScrollerCls || Ext.baseCSSPrefix + layout.owner.getXType() + '-scroll-' + layout.parallelAfter; + me.CSSinitializes = true; } - return { - width: width, - height: height - }; }, - invalidate: function(firstPass) { + handleOverflow: function(calculations, targetSize) { var me = this, - stretchEl = me.stretchEl; + layout = me.layout, + methodName = 'get' + layout.parallelPrefixCap, + newSize = {}; + + me.initCSSClasses(); + me.callParent(arguments); + this.createInnerElements(); + this.showScrollers(); + newSize[layout.perpendicularPrefix] = targetSize[layout.perpendicularPrefix]; + newSize[layout.parallelPrefix] = targetSize[layout.parallelPrefix] - (me.beforeCt[methodName]() + me.afterCt[methodName]()); + return { targetSize: newSize }; + }, - if (!stretchEl || !me.ownerCt) { - return; + /** + * @private + * Creates the beforeCt and afterCt elements if they have not already been created + */ + createInnerElements: function() { + var me = this, + target = me.layout.getRenderTarget(); + + //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of + //special items such as scrollers or dropdown menu triggers + if (!me.beforeCt) { + target.addCls(Ext.baseCSSPrefix + me.layout.direction + '-box-overflow-body'); + me.beforeCt = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.beforeCtCls}, 'before'); + me.afterCt = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.afterCtCls}, 'after'); + me.createWheelListener(); } + }, - var size = me.getSizeCalculation(), - scrollEl = me.scrollEl, - elDom = scrollEl.dom, - reservedSpace = me.reservedSpace, - pos, - extra = 5; - - if (size) { - stretchEl.setSize(size); - - size = me.el.getSize(true); + /** + * @private + * Sets up an listener to scroll on the layout's innerCt mousewheel event + */ + createWheelListener: function() { + this.layout.innerCt.on({ + scope : this, + mousewheel: function(e) { + e.stopEvent(); - if (me.vertical) { - size.width += extra; - size.height -= reservedSpace; - pos = 'left'; - } else { - size.width -= reservedSpace; - size.height += extra; - pos = 'top'; + this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false); } - - scrollEl.setSize(size); - elDom.style[pos] = (-extra) + 'px'; - - // BrowserBug: IE7 - // This makes the scroller enabled, when initially rendering. - elDom.scrollTop = elDom.scrollTop; - } + }); }, - afterComponentLayout: function() { - this.callParent(arguments); - this.invalidate(); + /** + * @private + */ + clearOverflow: function() { + this.hideScrollers(); }, - restoreScrollPos: function () { - var me = this, - el = this.scrollEl, - elDom = el && el.dom; + /** + * @private + * Shows the scroller elements in the beforeCt and afterCt. Creates the scrollers first if they are not already + * present. + */ + showScrollers: function() { + this.createScrollers(); + this.beforeScroller.show(); + this.afterScroller.show(); + this.updateScrollButtons(); + + this.layout.owner.addClsWithUI('scroller'); + }, - if (me._scrollPos !== null && elDom) { - elDom[me.scrollProp] = me._scrollPos; - me._scrollPos = null; + /** + * @private + * Hides the scroller elements in the beforeCt and afterCt + */ + hideScrollers: function() { + if (this.beforeScroller != undefined) { + this.beforeScroller.hide(); + this.afterScroller.hide(); + + this.layout.owner.removeClsWithUI('scroller'); } }, - setReservedSpace: function (reservedSpace) { - var me = this; - if (me.reservedSpace !== reservedSpace) { - me.reservedSpace = reservedSpace; - me.invalidate(); + /** + * @private + * Creates the clickable scroller elements and places them into the beforeCt and afterCt + */ + createScrollers: function() { + if (!this.beforeScroller && !this.afterScroller) { + var before = this.beforeCt.createChild({ + cls: Ext.String.format("{0} {1} ", this.scrollerCls, this.beforeScrollerCls) + }); + + var after = this.afterCt.createChild({ + cls: Ext.String.format("{0} {1}", this.scrollerCls, this.afterScrollerCls) + }); + + before.addClsOnOver(this.beforeScrollerCls + '-hover'); + after.addClsOnOver(this.afterScrollerCls + '-hover'); + + before.setVisibilityMode(Ext.Element.DISPLAY); + after.setVisibilityMode(Ext.Element.DISPLAY); + + this.beforeRepeater = Ext.create('Ext.util.ClickRepeater', before, { + interval: this.scrollRepeatInterval, + handler : this.scrollLeft, + scope : this + }); + + this.afterRepeater = Ext.create('Ext.util.ClickRepeater', after, { + interval: this.scrollRepeatInterval, + handler : this.scrollRight, + scope : this + }); + + /** + * @property beforeScroller + * @type Ext.Element + * The left scroller element. Only created when needed. + */ + this.beforeScroller = before; + + /** + * @property afterScroller + * @type Ext.Element + * The left scroller element. Only created when needed. + */ + this.afterScroller = after; } }, - saveScrollPos: function () { - var me = this, - el = this.scrollEl, - elDom = el && el.dom; - - me._scrollPos = elDom ? elDom[me.scrollProp] : null; + /** + * @private + */ + destroy: function() { + Ext.destroy(this.beforeRepeater, this.afterRepeater, this.beforeScroller, this.afterScroller, this.beforeCt, this.afterCt); }, - /** - * Sets the scrollTop and constrains the value between 0 and max. - * @param {Number} scrollTop - * @return {Number} The resulting scrollTop value after being constrained + /** + * @private + * Scrolls left or right by the number of pixels specified + * @param {Number} delta Number of pixels to scroll to the right by. Use a negative number to scroll left */ - setScrollTop: function(scrollTop) { - var el = this.scrollEl, - elDom = el && el.dom; - - if (elDom) { - return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight); - } + scrollBy: function(delta, animate) { + this.scrollTo(this.getScrollPosition() + delta, animate); }, - /** - * Sets the scrollLeft and constrains the value between 0 and max. - * @param {Number} scrollLeft - * @return {Number} The resulting scrollLeft value after being constrained + /** + * @private + * @return {Object} Object passed to scrollTo when scrolling */ - setScrollLeft: function(scrollLeft) { - var el = this.scrollEl, - elDom = el && el.dom; + getScrollAnim: function() { + return { + duration: this.scrollDuration, + callback: this.updateScrollButtons, + scope : this + }; + }, - if (elDom) { - return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth); + /** + * @private + * Enables or disables each scroller button based on the current scroll position + */ + updateScrollButtons: function() { + if (this.beforeScroller == undefined || this.afterScroller == undefined) { + return; } + + var beforeMeth = this.atExtremeBefore() ? 'addCls' : 'removeCls', + afterMeth = this.atExtremeAfter() ? 'addCls' : 'removeCls', + beforeCls = this.beforeScrollerCls + '-disabled', + afterCls = this.afterScrollerCls + '-disabled'; + + this.beforeScroller[beforeMeth](beforeCls); + this.afterScroller[afterMeth](afterCls); + this.scrolling = false; }, - /** - * Scroll by deltaY - * @param {Number} delta - * @return {Number} The resulting scrollTop value + /** + * @private + * Returns true if the innerCt scroll is already at its left-most point + * @return {Boolean} True if already at furthest left point */ - scrollByDeltaY: function(delta) { - var el = this.scrollEl, - elDom = el && el.dom; + atExtremeBefore: function() { + return this.getScrollPosition() === 0; + }, - if (elDom) { - return this.setScrollTop(elDom.scrollTop + delta); - } + /** + * @private + * Scrolls to the left by the configured amount + */ + scrollLeft: function() { + this.scrollBy(-this.scrollIncrement, false); }, - /** - * Scroll by deltaX - * @param {Number} delta - * @return {Number} The resulting scrollLeft value + /** + * @private + * Scrolls to the right by the configured amount */ - scrollByDeltaX: function(delta) { - var el = this.scrollEl, - elDom = el && el.dom; + scrollRight: function() { + this.scrollBy(this.scrollIncrement, false); + }, - if (elDom) { - return this.setScrollLeft(elDom.scrollLeft + delta); - } + /** + * Returns the current scroll position of the innerCt element + * @return {Number} The current scroll position + */ + getScrollPosition: function(){ + var layout = this.layout; + return parseInt(layout.innerCt.dom['scroll' + layout.parallelBeforeCap], 10) || 0; }, + /** + * @private + * Returns the maximum value we can scrollTo + * @return {Number} The max scroll value + */ + getMaxScrollPosition: function() { + var layout = this.layout; + return layout.innerCt.dom['scroll' + layout.parallelPrefixCap] - this.layout.innerCt['get' + layout.parallelPrefixCap](); + }, - /** - * Scroll to the top. + /** + * @private + * Returns true if the innerCt scroll is already at its right-most point + * @return {Boolean} True if already at furthest right point */ - scrollToTop : function(){ - this.setScrollTop(0); + atExtremeAfter: function() { + return this.getScrollPosition() >= this.getMaxScrollPosition(); }, - // synchronize the scroller with the bound gridviews - onElScroll: function(event, target) { - this.fireEvent('bodyscroll', event, target); + /** + * @private + * Scrolls to the given position. Performs bounds checking. + * @param {Number} position The position to scroll to. This is constrained. + * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll + */ + scrollTo: function(position, animate) { + var me = this, + layout = me.layout, + oldPosition = me.getScrollPosition(), + newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition()); + + if (newPosition != oldPosition && !me.scrolling) { + if (animate == undefined) { + animate = me.animateScroll; + } + + layout.innerCt.scrollTo(layout.parallelBefore, newPosition, animate ? me.getScrollAnim() : false); + if (animate) { + me.scrolling = true; + } else { + me.scrolling = false; + me.updateScrollButtons(); + } + + me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false); + } }, - getPanel: function() { - var me = this; - if (!me.panel) { - me.panel = this.up('[scrollerOwner]'); + /** + * Scrolls to the given component. + * @param {String/Number/Ext.Component} item The item to scroll to. Can be a numerical index, component id + * or a reference to the component itself. + * @param {Boolean} animate True to animate the scrolling + */ + scrollToItem: function(item, animate) { + var me = this, + layout = me.layout, + visibility, + box, + newPos; + + item = me.getItem(item); + if (item != undefined) { + visibility = this.getItemVisibility(item); + if (!visibility.fullyVisible) { + box = item.getBox(true, true); + newPos = box[layout.parallelPosition]; + if (visibility.hiddenEnd) { + newPos -= (this.layout.innerCt['get' + layout.parallelPrefixCap]() - box[layout.parallelPrefix]); + } + this.scrollTo(newPos, animate); + } } - return me.panel; - } -}); + }, -+ /** + * @private + * For a given item in the container, return an object with information on whether the item is visible + * with the current innerCt scroll value. + * @param {Ext.Component} item The item + * @return {Object} Values for fullyVisible, hiddenStart and hiddenEnd + */ + getItemVisibility: function(item) { + var me = this, + box = me.getItem(item).getBox(true, true), + layout = me.layout, + itemStart = box[layout.parallelPosition], + itemEnd = itemStart + box[layout.parallelPrefix], + scrollStart = me.getScrollPosition(), + scrollEnd = scrollStart + layout.innerCt['get' + layout.parallelPrefixCap](); + + return { + hiddenStart : itemStart < scrollStart, + hiddenEnd : itemEnd > scrollEnd, + fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd + }; + } +});