PDA

View Full Version : Always show scrollbar



Matt Jenkins
26 Aug 2013, 2:51 AM
I know there is some debate over the question of always showing the scrollbar but I recently worked on a project that needed to display the scroller when there is scrollable content.

I've only hacked the source but I thought I'd post it here for those interested (ST 2.2.1). All feedback welcome!

src/scroll/View.js


Ext.define('Ext.scroll.View', {
extend: 'Ext.Evented',
alternateClassName: 'Ext.util.ScrollView',
requires: [
'Ext.scroll.Scroller',
'Ext.scroll.Indicator'
],
config: {
/**
* @cfg {String} indicatorsUi
* The style of the indicators of this view. Available options are `dark` or `light`.
*/
indicatorsUi: 'dark',
element: null,
scroller: {},
indicators: {
x: {
axis: 'x'
},
y: {
axis: 'y'
}
},
indicatorsHidingDelay: 100,
cls: Ext.baseCSSPrefix + 'scroll-view',
alwaysShow: false
},
/**
* @method getScroller
* Returns the scroller instance in this view. Checkout the documentation of {@link Ext.scroll.Scroller} and
* {@link Ext.Container#getScrollable} for more information.
* @return {Ext.scroll.View} The scroller
*/


/**
* @private
*/
processConfig: function(config) {
if (!config) {
return null;
}


if (typeof config == 'string') {
config = {
direction: config
};
}


config = Ext.merge({}, config);


var scrollerConfig = config.scroller,
name;


if (!scrollerConfig) {
config.scroller = scrollerConfig = {};
}


for (name in config) {
if (config.hasOwnProperty(name)) {
if (!this.hasConfig(name)) {
scrollerConfig[name] = config[name];
delete config[name];
}
}
}


return config;
},
constructor: function(config) {
config = this.processConfig(config);


this.useIndicators = {x: true, y: true};


this.doHideIndicators = Ext.Function.bind(this.doHideIndicators, this);


this.initConfig(config);
},
setConfig: function(config) {
return this.callParent([this.processConfig(config)]);
},
updateIndicatorsUi: function(newUi) {


var indicators = this.getIndicators();
indicators.x.setUi(newUi);
indicators.y.setUi(newUi);






},
applyScroller: function(config, currentScroller) {
return Ext.factory(config, Ext.scroll.Scroller, currentScroller);
},
applyIndicators: function(config, indicators) {
var defaultClass = Ext.scroll.Indicator,
useIndicators = this.useIndicators;


if (!config) {
config = {};
}


if (!config.x) {
useIndicators.x = false;
config.x = {};
}


if (!config.y) {
useIndicators.y = false;
config.y = {};
}


return {
x: Ext.factory(config.x, defaultClass, indicators && indicators.x),
y: Ext.factory(config.y, defaultClass, indicators && indicators.y)
};




},
updateIndicators: function(indicators) {
this.indicatorsGrid = Ext.Element.create({
className: 'x-scroll-bar-grid-wrapper',
children: [{
className: 'x-scroll-bar-grid',
children: [
{
children: [{}, {
children: [indicators.y.barElement]
}]
},
{
children: [{
children: [indicators.x.barElement]
}, {}]
}
]
}]
});
},
updateScroller: function(scroller) {
scroller.on({
scope: this,
scrollstart: 'onScrollStart',
scroll: 'onScroll',
scrollend: 'onScrollEnd',
refresh: 'refreshIndicators'
});
},
isAxisEnabled: function(axis) {
return this.getScroller().isAxisEnabled(axis) && this.useIndicators[axis];
},
applyElement: function(element) {
if (element) {
return Ext.get(element);
}
},
updateElement: function(element) {
var scroller = this.getScroller(),
scrollerElement;




scrollerElement = element.getFirstChild().getFirstChild();
if (this.FixedHBoxStretching) {
scrollerElement = scrollerElement.getFirstChild();
}


element.addCls(this.getCls());
element.insertFirst(this.indicatorsGrid);


scroller.setElement(scrollerElement);


this.refreshIndicators();


return this;
},
showIndicators: function() {
var indicators = this.getIndicators();


if (this.hasOwnProperty('indicatorsHidingTimer')) {
clearTimeout(this.indicatorsHidingTimer);
delete this.indicatorsHidingTimer;
}


if (this.isAxisEnabled('x')) {
indicators.x.show();
}


if (this.isAxisEnabled('y')) {
indicators.y.show();
}
},
hideIndicators: function() {
var delay = this.getIndicatorsHidingDelay();


if (delay > 0) {
this.indicatorsHidingTimer = setTimeout(this.doHideIndicators, delay);
}
else {
this.doHideIndicators();
}
},
doHideIndicators: function() {

var indicators = this.getIndicators();

if (!this.getAlwaysShow() || ((indicators.x.gapLength === 0) && (indicators.y.gapLength === 0))) {


if (this.isAxisEnabled('x')) {
indicators.x.hide();
}


if (this.isAxisEnabled('y')) {
indicators.y.hide();
}
}
},
onScrollStart: function() {
this.onScroll.apply(this, arguments);
this.showIndicators();
},
onScrollEnd: function() {
this.hideIndicators();
},
onScroll: function(scroller, x, y) {
this.setIndicatorValue('x', x);
this.setIndicatorValue('y', y);


//<debug>
if (this.isBenchmarking) {
this.framesCount++;
}
//</debug>
},
//<debug>
isBenchmarking: false,
framesCount: 0,
getCurrentFps: function() {
var now = Date.now(),
fps;


if (!this.isBenchmarking) {
this.isBenchmarking = true;
fps = 0;
}
else {
fps = Math.round(this.framesCount * 1000 / (now - this.framesCountStartTime));
}


this.framesCountStartTime = now;
this.framesCount = 0;


return fps;
},
//</debug>


setIndicatorValue: function(axis, scrollerPosition) {
if (!this.isAxisEnabled(axis)) {
return this;
}


var scroller = this.getScroller(),
scrollerMaxPosition = scroller.getMaxPosition()[axis],
scrollerContainerSize = scroller.getContainerSize()[axis],
value;


if (scrollerMaxPosition === 0) {
value = scrollerPosition / scrollerContainerSize;


if (scrollerPosition >= 0) {
value += 1;
}
}
else {
if (scrollerPosition > scrollerMaxPosition) {
value = 1 + ((scrollerPosition - scrollerMaxPosition) / scrollerContainerSize);
}
else if (scrollerPosition < 0) {
value = scrollerPosition / scrollerContainerSize;
}
else {
value = scrollerPosition / scrollerMaxPosition;
}
}


this.getIndicators()[axis].setValue(value);
},
refreshIndicator: function(axis) {
if (!this.isAxisEnabled(axis)) {
return this;
}


var scroller = this.getScroller(),
indicator = this.getIndicators()[axis],
scrollerContainerSize = scroller.getContainerSize()[axis],
scrollerSize = scroller.getSize()[axis],
ratio = scrollerContainerSize / scrollerSize;


indicator.setRatio(ratio);
indicator.refresh();
},
refresh: function() {
return this.getScroller().refresh();
},
refreshIndicators: function() {
var indicators = this.getIndicators();


indicators.x.setActive(this.isAxisEnabled('x'));
indicators.y.setActive(this.isAxisEnabled('y'));


this.refreshIndicator('x');
this.refreshIndicator('y');


if (this.getAlwaysShow() && (indicators.x.gapLength || indicators.y.gapLength)) {
this.showIndicators();
}
},
destroy: function() {
var element = this.getElement(),
indicators = this.getIndicators();


Ext.destroy(this.getScroller(), this.indicatorsGrid);


if (this.hasOwnProperty('indicatorsHidingTimer')) {
clearTimeout(this.indicatorsHidingTimer);
delete this.indicatorsHidingTimer;
}


if (element && !element.isDestroyed) {
element.removeCls(this.getCls());
}


indicators.x.destroy();
indicators.y.destroy();


delete this.indicatorsGrid;


this.callParent(arguments);
}
});


Usage


scrollable: {
alwaysShow: true
}

fmoseley
27 Aug 2013, 5:48 PM
Thank you for sharing.