UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

436 lines (381 loc) 11 kB
/* ************************************************************************ 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() { super(); 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() { this.setValue(this.getValue() + this.getStep()); }, /** * Decrements the current value. */ previousValue() { this.setValue(this.getValue() - this.getStep()); }, // overridden _createContainerElement() { var container = super._createContainerElement(); container.appendChild(this._createKnobElement()); return container; }, /** * Creates the knob element. * * @return {Element} The created knob element */ _createKnobElement() { return qx.dom.Element.create("div"); }, /** * Registers all needed event listener. */ _registerEventListener() { 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() { 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() { this._updateSizes(); this._updateKnobPosition(); }, /** * Updates all internal sizes of the slider. */ _updateSizes() { 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(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(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(documentLeft) { return documentLeft - this._containerElementLeft; }, /** * Returns the knob DOM element. * * @return {Element} The knob DOM element. */ _getKnobElement() { 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(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() { return this.__value; }, /** * Updates the knob position based on the current value. */ _updateKnobPosition() { 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(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(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(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(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(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(width) { return width / this._getOverallSteps(); }, /** * Return the overall number of steps. * * @return {Integer} The number of steps */ _getOverallSteps() { 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() { return this.getMaximum() - this.getMinimum(); } }, destruct() { this._knobElement = null; this._unregisterEventListener(); } });