@qooxdoo/framework
Version:
The JS Framework for Coders
464 lines (385 loc) • 11.2 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2011 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:
* Tino Butz (tbtz)
************************************************************************ */
/**
* The Slider widget provides horizontal slider.
*
* The Slider is the classic widget for controlling a bounded value.
* It lets the user move a slider handle along a horizontal
* groove and translates the handle's position into an integer value
* within the defined range.
*
* *Example*
*
* Here is a little example of how to use the widget.
*
* <pre class='javascript'>
* var slider= new qx.ui.mobile.form.Slider().set({
* minimum : 0,
* maximum : 10,
* step : 2
* });
* slider.addListener("changeValue", handler, this);
*
* this.getRoot.add(slider);
* </pre>
*
* This example creates a slider and attaches an
* event listener to the {@link #changeValue} event.
*/
qx.Class.define("qx.ui.mobile.form.Slider",
{
extend : qx.ui.mobile.core.Widget,
include : [
qx.ui.mobile.form.MValue,
qx.ui.form.MForm,
qx.ui.form.MModelProperty,
qx.ui.mobile.form.MState
],
implement : [
qx.ui.form.IForm,
qx.ui.form.IModel,
qx.ui.form.INumberForm
],
construct : function()
{
this.base(arguments);
this._registerEventListener();
this._refresh();
this.addCssClass("gap");
},
properties :
{
// overridden
defaultCssClass :
{
refine : true,
init : "slider"
},
/**
* The minimum slider value (may be negative). This value must be smaller
* than {@link #maximum}.
*/
minimum :
{
check : "Number",
init : 0,
apply : "_refresh",
event : "changeMinimum"
},
/**
* The maximum slider value (may be negative). This value must be larger
* than {@link #minimum}.
*/
maximum :
{
check : "Number",
init : 100,
apply : "_refresh",
event : "changeMaximum"
},
/**
* The amount to increment on each event. Typically corresponds
* to the user moving the knob.
*/
step :
{
check : "Number",
init : 1,
event : "changeStep"
},
/**
* Reverses the display direction of the slider knob. If true, the maxmium of
* the slider is on the left side and minimum on the right side.
*/
reverseDirection :
{
check : "Boolean",
init : false,
apply : "_refresh"
},
/**
* Adjusts which slider value should be displayed inside the knob.
* If <code>null</code> no value will be displayed.
*/
displayValue :
{
init : "percent",
check : [ "value", "percent" ],
nullable : true,
apply : "_applyDisplayValue"
}
},
members :
{
_knobElement : null,
_containerElementWidth : null,
_containerElementLeft : null,
_pixelPerStep : null,
__value: 0,
/**
* Increments the current value.
*/
nextValue : function() {
this.setValue(this.getValue() + this.getStep());
},
/**
* Decrements the current value.
*/
previousValue : function() {
this.setValue(this.getValue() - this.getStep());
},
// overridden
_createContainerElement : function()
{
var container = this.base(arguments);
container.appendChild(this._createKnobElement());
return container;
},
/**
* Creates the knob element.
*
* @return {Element} The created knob element
*/
_createKnobElement : function()
{
return qx.dom.Element.create("div");
},
/**
* Registers all needed event listener.
*/
_registerEventListener : function()
{
this.addListener("pointerdown", this._onPointerDown, this);
this.addListener("track", this._onTrack, this);
this.addListener("appear", this._refresh, this);
qx.event.Registration.addListener(window, "resize", this._refresh, this);
qx.event.Registration.addListener(window, "orientationchange", this._refresh, this);
this.addListenerOnce("domupdated", this._refresh, this);
},
/**
* Unregisters all needed event listener.
*/
_unregisterEventListener : function()
{
this.removeListener("pointerdown", this._onPointerDown, this);
this.removeListener("track", this._onTrack, this);
this.removeListener("appear", this._refresh, this);
qx.event.Registration.removeListener(window, "resize", this._refresh, this);
qx.event.Registration.removeListener(window, "orientationchange", this._refresh, this);
this.removeListener("domupdated", this._refresh, this);
},
/**
* Refreshes the slider and the knob position.
*/
_refresh : function()
{
this._updateSizes();
this._updateKnobPosition();
},
/**
* Updates all internal sizes of the slider.
*/
_updateSizes : function()
{
var containerElement = this.getContainerElement();
if(containerElement) {
this._containerElementWidth = qx.bom.element.Dimension.getWidth(containerElement);
this._containerElementLeft = qx.bom.element.Location.getLeft(containerElement);
this._pixelPerStep = this._getPixelPerStep(this._containerElementWidth);
}
},
/**
* Event handler. Called when the <code>pointerdown</code> event occurs.
*
* @param evt {qx.event.type.Pointer} The pointer event.
*/
_onPointerDown: function(evt)
{
if (evt.isPrimary())
{
this._updateSizes();
var position = this._getPosition(evt.getDocumentLeft());
this.setValue(this._positionToValue(position));
evt.stopPropagation();
}
},
/**
* Event handler. Called when the <code>track</code> event occurs.
*
* @param evt {qx.event.type.Track} The track event.
*/
_onTrack : function(evt)
{
var position = this._getPosition(evt.getDocumentLeft());
this.setValue(this._positionToValue(position));
evt.stopPropagation();
evt.preventDefault();
},
/**
* Returns the current position of the knob.
*
* @param documentLeft {Integer} The left position of the knob
* @return {Integer} The current position of the container element.
*/
_getPosition : function(documentLeft)
{
return documentLeft - this._containerElementLeft;
},
/**
* Returns the knob DOM element.
*
* @return {Element} The knob DOM element.
*/
_getKnobElement : function()
{
if (!this._knobElement) {
var element = this.getContainerElement();
if (element) {
this._knobElement = element.childNodes[0];
}
}
return this._knobElement;
},
/**
* Sets the value of this slider.
* It is called by setValue method of qx.ui.mobile.form.MValue mixin
* @param value {Integer} the new value of the slider
*/
_setValue : function(value)
{
this.__value = value;
qx.bom.AnimationFrame.request(this._refresh, this);
},
/**
* Gets the value [true/false] of this slider.
* It is called by getValue method of qx.ui.mobile.form.MValue mixin
* @return {Integer} the value of the slider
*/
_getValue : function() {
return this.__value;
},
/**
* Updates the knob position based on the current value.
*/
_updateKnobPosition : function()
{
var percent = this._valueToPercent(this.getValue());
var width = this._containerElementWidth;
var position = Math.floor(this._percentToPosition(width, percent));
var knobElement = this._getKnobElement();
if (knobElement) {
qx.bom.element.Style.set(this._getKnobElement(), "width", width - (width - position) + "px");
qx.bom.element.Attribute.set(this._getKnobElement(), "data-value", this.getValue());
qx.bom.element.Attribute.set(this._getKnobElement(), "data-percent", Math.floor(percent));
}
},
// Property apply
_applyDisplayValue : function(value, old ) {
if(old != null) {
this.removeCssClass(old);
}
if(value != null) {
this.addCssClass(value);
}
},
/**
* Converts the given value to percent.
*
* @param value {Integer} The value to convert
* @return {Integer} The value in percent
*/
_valueToPercent : function(value)
{
var min = this.getMinimum();
var value = this._limitValue(value);
var percent = ((value - min) * 100) / this._getRange();
if (this.isReverseDirection()) {
return 100 - percent;
} else {
return percent;
}
},
/**
* Converts the given position to the corresponding value.
*
* @param position {Integer} The position to convert
* @return {Integer} The converted value
*/
_positionToValue : function(position)
{
var value = this.getMinimum() + (Math.round(position / this._pixelPerStep) * this.getStep());
value = this._limitValue(value);
if(this.isReverseDirection()) {
var center = this.getMinimum() + this._getRange()/2;
var dist = center-value;
value = center + dist;
}
return value;
},
/**
* Converts the given percent to the position of the knob.
*
* @param width {Integer} The width of the slider container element
* @param percent {Integer} The percent to convert
* @return {Integer} The position of the knob
*/
_percentToPosition : function(width, percent)
{
return width * (percent / 100);
},
/**
* Limits a value to the set {@link #minimum} and {@link #maximum} properties.
*
* @param value {Integer} The value to limit
* @return {Integer} The limited value
*/
_limitValue : function(value)
{
value = Math.min(value, this.getMaximum());
value = Math.max(value, this.getMinimum());
return value;
},
/**
* Return the number of pixels per step.
*
* @param width {Integer} The width of the slider container element
* @return {Integer} The pixels per step
*/
_getPixelPerStep : function(width)
{
return width / this._getOverallSteps();
},
/**
* Return the overall number of steps.
*
* @return {Integer} The number of steps
*/
_getOverallSteps : function()
{
return (this._getRange() / this.getStep());
},
/**
* Return the range between {@link #maximum} and {@link #minimum}.
*
* @return {Integer} The range between {@link #maximum} and {@link #minimum}
*/
_getRange : function()
{
return this.getMaximum() - this.getMinimum();
}
},
destruct : function()
{
this._knobElement = null;
this._unregisterEventListener();
}
});