@qooxdoo/framework
Version:
The JS Framework for Coders
410 lines (327 loc) • 10.2 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2009 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Fabian Jakobs (fjakobs)
************************************************************************ */
/**
* The scroll bar widget wraps the native browser scroll bars as a qooxdoo widget.
* It can be uses instead of the styled qooxdoo scroll bars.
*
* Scroll bars are used by the {@link qx.ui.container.Scroll} container. Usually
* a scroll bar is not used directly.
*
* *Example*
*
* Here is a little example of how to use the widget.
*
* <pre class='javascript'>
* var scrollBar = new qx.ui.core.scroll.NativeScrollBar("horizontal");
* scrollBar.set({
* maximum: 500
* })
* this.getRoot().add(scrollBar);
* </pre>
*
* This example creates a horizontal scroll bar with a maximum value of 500.
*
* *External Documentation*
*
* <a href='http://manual.qooxdoo.org/${qxversion}/pages/widget/scrollbar.html' target='_blank'>
* Documentation of this widget in the qooxdoo manual.</a>
*/
qx.Class.define("qx.ui.core.scroll.NativeScrollBar",
{
extend : qx.ui.core.Widget,
implement : qx.ui.core.scroll.IScrollBar,
/**
* @param orientation {String?"horizontal"} The initial scroll bar orientation
*/
construct : function(orientation)
{
this.base(arguments);
this.addState("native");
this.getContentElement().addListener("scroll", this._onScroll, this);
this.addListener("pointerdown", this._stopPropagation, this);
this.addListener("pointerup", this._stopPropagation, this);
this.addListener("pointermove", this._stopPropagation, this);
this.addListener("appear", this._onAppear, this);
this.getContentElement().add(this._getScrollPaneElement());
this.getContentElement().setStyle("box-sizing", "content-box");
// Configure orientation
if (orientation != null) {
this.setOrientation(orientation);
} else {
this.initOrientation();
}
// prevent drag & drop on scrolling
this.addListener("track", function(e) {
e.stopPropagation();
}, this);
},
events : {
/**
* Fired as soon as the scroll animation ended.
*/
scrollAnimationEnd: 'qx.event.type.Event'
},
properties :
{
// overridden
appearance :
{
refine : true,
init : "scrollbar"
},
// interface implementation
orientation :
{
check : [ "horizontal", "vertical" ],
init : "horizontal",
apply : "_applyOrientation"
},
// interface implementation
maximum :
{
check : "PositiveInteger",
apply : "_applyMaximum",
init : 100
},
// interface implementation
position :
{
check : "Number",
init : 0,
apply : "_applyPosition",
event : "scroll"
},
/**
* Step size for each tap on the up/down or left/right buttons.
*/
singleStep :
{
check : "Integer",
init : 20
},
// interface implementation
knobFactor :
{
check : "PositiveNumber",
nullable : true
}
},
members :
{
__isHorizontal : null,
__scrollPaneElement : null,
__requestId : null,
__scrollAnimationframe : null,
/**
* Get the scroll pane html element.
*
* @return {qx.html.Element} The element
*/
_getScrollPaneElement : function()
{
if (!this.__scrollPaneElement) {
this.__scrollPaneElement = new qx.html.Element();
}
return this.__scrollPaneElement;
},
/*
---------------------------------------------------------------------------
WIDGET API
---------------------------------------------------------------------------
*/
// overridden
renderLayout : function(left, top, width, height)
{
var changes = this.base(arguments, left, top, width, height);
this._updateScrollBar();
return changes;
},
// overridden
_getContentHint : function()
{
var scrollbarWidth = qx.bom.element.Scroll.getScrollbarWidth();
return {
width: this.__isHorizontal ? 100 : scrollbarWidth,
maxWidth: this.__isHorizontal ? null : scrollbarWidth,
minWidth: this.__isHorizontal ? null : scrollbarWidth,
height: this.__isHorizontal ? scrollbarWidth : 100,
maxHeight: this.__isHorizontal ? scrollbarWidth : null,
minHeight: this.__isHorizontal ? scrollbarWidth : null
};
},
// overridden
_applyEnabled : function(value, old)
{
this.base(arguments, value, old);
this._updateScrollBar();
},
/*
---------------------------------------------------------------------------
PROPERTY APPLY ROUTINES
---------------------------------------------------------------------------
*/
// property apply
_applyMaximum : function(value) {
this._updateScrollBar();
},
// property apply
_applyPosition : function(value)
{
var content = this.getContentElement();
if (this.__isHorizontal) {
content.scrollToX(value);
} else {
content.scrollToY(value);
}
},
// property apply
_applyOrientation : function(value, old)
{
var isHorizontal = this.__isHorizontal = value === "horizontal";
this.set({
allowGrowX : isHorizontal,
allowShrinkX : isHorizontal,
allowGrowY : !isHorizontal,
allowShrinkY : !isHorizontal
});
if (isHorizontal) {
this.replaceState("vertical", "horizontal");
} else {
this.replaceState("horizontal", "vertical");
}
this.getContentElement().setStyles({
overflowX: isHorizontal ? "scroll" : "hidden",
overflowY: isHorizontal ? "hidden" : "scroll"
});
// Update layout
qx.ui.core.queue.Layout.add(this);
},
/**
* Update the scroll bar according to its current size, max value and
* enabled state.
*/
_updateScrollBar : function()
{
var isHorizontal = this.__isHorizontal;
var bounds = this.getBounds();
if (!bounds) {
return;
}
if (this.isEnabled())
{
var containerSize = isHorizontal ? bounds.width : bounds.height;
var innerSize = this.getMaximum() + containerSize;
} else {
innerSize = 0;
}
// Scrollbars don't work properly in IE/Edge if the element with overflow has
// exactly the size of the scrollbar. Thus we move the element one pixel
// out of the view and increase the size by one.
if (qx.core.Environment.get("engine.name") == "mshtml" ||
qx.core.Environment.get("browser.name") == "edge")
{
var bounds = this.getBounds();
this.getContentElement().setStyles({
left: (isHorizontal ? bounds.left : (bounds.left -1)) + "px",
top: (isHorizontal ? (bounds.top - 1) : bounds.top) + "px",
width: (isHorizontal ? bounds.width : bounds.width + 1) + "px",
height: (isHorizontal ? bounds.height + 1 : bounds.height) + "px"
});
}
this._getScrollPaneElement().setStyles({
left: 0,
top: 0,
width: (isHorizontal ? innerSize : 1) + "px",
height: (isHorizontal ? 1 : innerSize) + "px"
});
this.updatePosition(this.getPosition());
},
// interface implementation
scrollTo : function(position, duration) {
// if a user sets a new position, stop any animation
this.stopScrollAnimation();
if (duration) {
var from = this.getPosition();
this.__scrollAnimationframe = new qx.bom.AnimationFrame();
this.__scrollAnimationframe.on("frame", function(timePassed) {
var newPos = parseInt(timePassed/duration * (position - from) + from);
this.updatePosition(newPos);
}, this);
this.__scrollAnimationframe.on("end", function() {
this.setPosition(Math.max(0, Math.min(this.getMaximum(), position)));
this.__scrollAnimationframe = null;
this.fireEvent("scrollAnimationEnd");
}, this);
this.__scrollAnimationframe.startSequence(duration);
} else {
this.updatePosition(position);
}
},
/**
* Helper to set the new position taking care of min and max values.
* @param position {Number} The new position.
*/
updatePosition : function(position) {
this.setPosition(Math.max(0, Math.min(this.getMaximum(), position)));
},
// interface implementation
scrollBy : function(offset, duration) {
this.scrollTo(this.getPosition() + offset, duration);
},
// interface implementation
scrollBySteps : function(steps, duration)
{
var size = this.getSingleStep();
this.scrollBy(steps * size, duration);
},
/**
* If a scroll animation is running, it will be stopped.
*/
stopScrollAnimation : function() {
if (this.__scrollAnimationframe) {
this.__scrollAnimationframe.cancelSequence();
this.__scrollAnimationframe = null;
}
},
/**
* Scroll event handler
*
* @param e {qx.event.type.Event} the scroll event
*/
_onScroll : function(e)
{
var container = this.getContentElement();
var position = this.__isHorizontal ? container.getScrollX() : container.getScrollY();
this.setPosition(position);
},
/**
* Listener for appear which ensured the scroll bar is positioned right
* on appear.
*
* @param e {qx.event.type.Data} Incoming event object
*/
_onAppear : function(e) {
this._applyPosition(this.getPosition());
},
/**
* Stops propagation on the given even
*
* @param e {qx.event.type.Event} the event
*/
_stopPropagation : function(e) {
e.stopPropagation();
}
},
destruct : function() {
this._disposeObjects("__scrollPaneElement");
}
});