]>
git.proxmox.com Git - extjs.git/blob - extjs/classic/classic/src/layout/container/boxOverflow/Scroller.js
4 Ext
.define('Ext.layout.container.boxOverflow.Scroller', {
6 /* Begin Definitions */
8 extend
: 'Ext.layout.container.boxOverflow.None',
9 requires
: ['Ext.util.ClickRepeater', 'Ext.Element'],
10 alternateClassName
: 'Ext.layout.boxOverflow.Scroller',
12 'box.overflow.scroller',
13 'box.overflow.Scroller' // capitalized for 4.x compat
16 observable
: 'Ext.mixin.Observable'
22 * @cfg {Boolean} animateScroll
23 * True to animate the scrolling of items within the layout (ignored if enableScroll is false)
28 * @cfg {Number} scrollIncrement
29 * The number of pixels to scroll by on scroller click
34 * @cfg {Number} wheelIncrement
35 * The number of pixels to increment on mouse wheel scrolling.
40 * @cfg {Number} scrollRepeatInterval
41 * Number of milliseconds between each scroll while a scroller button is held down
43 scrollRepeatInterval
: 60,
46 * @cfg {Number} scrollDuration
47 * Number of milliseconds that each scroll animation lasts
54 scrollerCls
: Ext
.baseCSSPrefix
+ 'box-scroller',
55 beforeSuffix
: '-before-scroller',
56 afterSuffix
: '-after-scroller',
60 * @param {Ext.layout.container.boxOverflow.Scroller} scroller The layout scroller
61 * @param {Number} newPosition The new position of the scroller
62 * @param {Boolean/Object} animate If animating or not. If true, it will be a animation configuration, else it will be false
65 constructor: function(config
) {
68 me
.mixins
.observable
.constructor.call(me
, config
);
70 me
.scrollPosition
= 0;
74 getPrefixConfig: function() {
77 id
: this.layout
.owner
.id
+ this.beforeSuffix
,
78 cls
: this.createScrollerCls('beforeX'),
83 getSuffixConfig: function() {
86 id
: this.layout
.owner
.id
+ this.afterSuffix
,
87 cls
: this.createScrollerCls('afterX'),
92 createScrollerCls: function(xName
) {
96 type
= me
.getOwnerType(owner
),
97 scrollerCls
= me
.scrollerCls
,
100 scrollerCls
+ '-' + layout
.names
[xName
] + ' ' +
101 scrollerCls
+ '-' + type
+ ' ' +
102 scrollerCls
+ '-' + type
+ '-' + owner
.ui
;
105 // Add plain class for components that need separate "plain" styling (e.g. tab bar)
106 cls
+= ' ' + scrollerCls
+ '-plain';
112 getOverflowCls: function(direction
) {
113 return this.scrollerCls
+ '-body-' + direction
;
116 beginLayout: function (ownerContext
) {
117 ownerContext
.innerCtScrollPos
= this.getScrollPosition();
119 this.callParent(arguments
);
122 finishedLayout: function(ownerContext
) {
124 plan
= ownerContext
.state
.boxPlan
,
126 names
= layout
.names
,
127 scrollPos
= Math
.min(me
.getMaxScrollPosition(), ownerContext
.innerCtScrollPos
),
130 // If there is overflow...
131 if (plan
&& plan
.tooNarrow
) {
132 lastProps
= ownerContext
.childItems
[ownerContext
.childItems
.length
- 1].props
;
134 // capture this before callParent since it calls handle/clearOverflow:
135 me
.scrollSize
= lastProps
[names
.x
] + lastProps
[names
.width
];
136 me
.updateScrollButtons();
138 // Restore pre layout scroll position
139 layout
.innerCt
[names
.setScrollLeft
](scrollPos
);
142 me
.callParent([ownerContext
]);
145 handleOverflow: function(ownerContext
) {
147 names
= me
.layout
.names
,
148 getWidth
= names
.getWidth
,
149 parallelMargins
= names
.parallelMargins
,
150 scrollerWidth
, targetPaddingWidth
, beforeScroller
, afterScroller
;
154 beforeScroller
= me
.getBeforeScroller();
155 afterScroller
= me
.getAfterScroller();
157 scrollerWidth
= beforeScroller
[getWidth
]() + afterScroller
[getWidth
]() +
158 beforeScroller
.getMargin(parallelMargins
) + afterScroller
.getMargin(parallelMargins
);
160 targetPaddingWidth
= ownerContext
.targetContext
.getPaddingInfo()[names
.width
];
163 reservedSpace
: Math
.max(scrollerWidth
- targetPaddingWidth
, 0)
169 * Returns a reference to the "before" scroller element. Creates click handlers on
172 getBeforeScroller: function() {
175 return me
._beforeScroller
|| (me
._beforeScroller
=
176 me
.createScroller(me
.beforeSuffix
, 'beforeRepeater', 'scrollLeft'));
181 * Returns a reference to the "after" scroller element. Creates click handlers on
184 getAfterScroller: function() {
187 return me
._afterScroller
|| (me
._afterScroller
=
188 me
.createScroller(me
.afterSuffix
, 'afterRepeater', 'scrollRight'));
191 createScroller: function(suffix
, repeaterName
, scrollHandler
) {
193 owner
= me
.layout
.owner
,
194 scrollerCls
= me
.scrollerCls
,
197 scrollerEl
= owner
.el
.getById(owner
.id
+ suffix
);
199 scrollerEl
.addClsOnOver(scrollerCls
+ '-hover');
200 scrollerEl
.addClsOnClick(scrollerCls
+ '-pressed');
202 scrollerEl
.setVisibilityMode(Ext
.Element
.DISPLAY
);
204 me
[repeaterName
] = new Ext
.util
.ClickRepeater(scrollerEl
, {
205 interval
: me
.scrollRepeatInterval
,
206 handler
: scrollHandler
,
215 * Sets up an listener to scroll on the layout's innerCt mousewheel event
217 createWheelListener: function() {
219 me
.wheelListener
= me
.layout
.innerCt
.on('mousewheel', me
.onMouseWheel
, me
, {destroyable
: true});
222 onMouseWheel: function(e
) {
224 this.scrollBy(this.getWheelDelta(e
) * this.wheelIncrement
* -1, false);
227 getWheelDelta: function (e
) {
228 return e
.getWheelDelta();
234 clearOverflow: function () {
235 this.hideScrollers();
240 * Shows the scroller elements. Creates the scrollers first if they are not already present.
242 showScrollers: function() {
245 if (!me
.wheelListener
) {
246 me
.createWheelListener();
248 me
.getBeforeScroller().show();
249 me
.getAfterScroller().show();
250 me
.layout
.owner
.addClsWithUI(me
.layout
.direction
=== 'vertical' ? 'vertical-scroller' : 'scroller');
251 // TODO - this may invalidates data in the ContextItem's styleCache
256 * Hides the scroller elements.
258 hideScrollers: function() {
260 beforeScroller
= me
.getBeforeScroller(),
261 afterScroller
= me
.getAfterScroller();
263 if (beforeScroller
) {
264 beforeScroller
.hide();
265 afterScroller
.hide();
266 me
.layout
.owner
.removeClsWithUI(me
.layout
.direction
=== 'vertical' ? 'vertical-scroller' : 'scroller');
267 // TODO - this may invalidates data in the ContextItem's styleCache
271 destroy: function() {
272 Ext
.destroyMembers(this, 'beforeRepeater', 'afterRepeater', '_beforeScroller', '_afterScroller', 'wheelListener');
278 * Scrolls left or right by the number of pixels specified
279 * @param {Number} delta Number of pixels to scroll to the right by. Use a negative number to scroll left
281 scrollBy: function(delta
, animate
) {
282 this.scrollTo(this.getScrollPosition() + delta
, animate
);
287 * @return {Object} Object passed to scrollTo when scrolling
289 getScrollAnim: function() {
291 duration
: this.scrollDuration
,
292 callback
: this.updateScrollButtons
,
299 * Enables or disables each scroller button based on the current scroll position
301 updateScrollButtons: function() {
303 beforeScroller
= me
.getBeforeScroller(),
304 afterScroller
= me
.getAfterScroller(),
307 if (!beforeScroller
|| !afterScroller
) {
311 disabledCls
= me
.scrollerCls
+ '-disabled';
313 beforeScroller
[me
.atExtremeBefore() ? 'addCls' : 'removeCls'](disabledCls
);
314 afterScroller
[me
.atExtremeAfter() ? 'addCls' : 'removeCls'](disabledCls
);
315 me
.scrolling
= false;
320 * Scrolls to the left by the configured amount
322 scrollLeft: function() {
323 this.scrollBy(-this.scrollIncrement
, false);
328 * Scrolls to the right by the configured amount
330 scrollRight: function() {
331 this.scrollBy(this.scrollIncrement
, false);
335 * Returns the current scroll position of the innerCt element
336 * @return {Number} The current scroll position
338 getScrollPosition: function(){
343 // Until we actually scroll, the scroll[Top|Left] is stored as zero to avoid DOM
344 // hits, after that it's NaN.
345 if (isNaN(me
.scrollPosition
)) {
346 result
= layout
.innerCt
[layout
.names
.getScrollLeft
]();
348 result
= me
.scrollPosition
;
355 * Returns the maximum value we can scrollTo
356 * @return {Number} The max scroll value
358 getMaxScrollPosition: function() {
361 maxScrollPos
= me
.scrollSize
- layout
.innerCt
.lastBox
[layout
.names
.width
];
363 return (maxScrollPos
< 0) ? 0 : maxScrollPos
;
368 * Returns true if the innerCt scroll is already at its left-most point
369 * @return {Boolean} True if already at furthest left point
371 atExtremeBefore: function() {
372 return !this.getScrollPosition();
377 * Returns true if the innerCt scroll is already at its right-most point
378 * @return {Boolean} True if already at furthest right point
380 atExtremeAfter: function() {
381 return this.getScrollPosition() >= this.getMaxScrollPosition();
387 setVertical: function() {
389 beforeScroller
= me
.getBeforeScroller(),
390 afterScroller
= me
.getAfterScroller(),
391 names
= me
.layout
.names
,
392 scrollerCls
= me
.scrollerCls
;
394 beforeScroller
.removeCls(scrollerCls
+ '-' + names
.beforeY
);
395 afterScroller
.removeCls(scrollerCls
+ '-' + names
.afterY
);
397 beforeScroller
.addCls(scrollerCls
+ '-' + names
.beforeX
);
398 afterScroller
.addCls(scrollerCls
+ '-' + names
.afterX
);
405 * Scrolls to the given position. Performs bounds checking.
406 * @param {Number} position The position to scroll to. This is constrained.
407 * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
409 scrollTo: function(position
, animate
) {
412 names
= layout
.names
,
413 oldPosition
= me
.getScrollPosition(),
414 newPosition
= Ext
.Number
.constrain(position
, 0, me
.getMaxScrollPosition());
416 if (newPosition
!== oldPosition
&& !me
.scrolling
) {
417 me
.scrollPosition
= NaN
;
418 if (animate
=== undefined) {
419 animate
= me
.animateScroll
;
422 layout
.innerCt
[names
.scrollTo
](names
.beforeScrollX
, newPosition
, animate
? me
.getScrollAnim() : false);
426 me
.updateScrollButtons();
428 me
.fireEvent('scroll', me
, newPosition
, animate
? me
.getScrollAnim() : false);
433 * Scrolls to the given component.
434 * @param {String/Number/Ext.Component} item The item to scroll to. Can be a numerical index, component id
435 * or a reference to the component itself.
436 * @param {Boolean} animate True to animate the scrolling
438 scrollToItem: function(item
, animate
) {
441 owner
= layout
.owner
,
442 names
= layout
.names
,
443 innerCt
= layout
.innerCt
,
448 item
= me
.getItem(item
);
449 if (item
!== undefined) {
450 if (item
=== owner
.items
.first()) {
452 } else if (item
=== owner
.items
.last()) {
453 newPos
= me
.getMaxScrollPosition();
455 visibility
= me
.getItemVisibility(item
);
456 if (!visibility
.fullyVisible
) {
457 box
= item
.getBox(false, true);
458 newPos
= box
[names
.x
];
459 if (visibility
.hiddenEnd
) {
460 newPos
-= (innerCt
[names
.getWidth
]() - box
[names
.width
]);
464 if (newPos
!== undefined) {
465 me
.scrollTo(newPos
, animate
);
472 * For a given item in the container, return an object with information on whether the item is visible
473 * with the current innerCt scroll value.
474 * @param {Ext.Component} item The item
475 * @return {Object} Values for fullyVisible, hiddenStart and hiddenEnd
477 getItemVisibility: function(item
) {
479 box
= me
.getItem(item
).getBox(true, true),
481 names
= layout
.names
,
482 itemStart
= box
[names
.x
],
483 itemEnd
= itemStart
+ box
[names
.width
],
484 scrollStart
= me
.getScrollPosition(),
485 scrollEnd
= scrollStart
+ layout
.innerCt
[names
.getWidth
]();
488 hiddenStart
: itemStart
< scrollStart
,
489 hiddenEnd
: itemEnd
> scrollEnd
,
490 fullyVisible
: itemStart
>= scrollStart
&& itemEnd
<= scrollEnd