Source: UI/UISlider.js

/* UISlider.js
 * 
 */

"use strict";

/**
 * A Slider to be used for UIs
 * @param {Array[]} position Base position for the UISlider
 * @param {Array[]} size The size for the UISlider
 * @class UISlider
 * @returns {UISlider}
 */
function UISlider(position, size) {
    UIBar.call(this, position, size);
    // hide the middle element of the UIBar
    this.setMidVisible(false);
    // turn off interpolation of the UIBar
    this.configInterpolation(1, 1);
    
    this.mClick = false;
    
    this.mMaxPos = position[0] + (size[0]/2);
    this.mMinPos = position[0] - (size[0]/2);
    
    // default Top Elem color = darker blue
    this.setTopElemColor([0,0,0.8,1]);
    
    // default handle for slider
    this.mHandle = new UITexture("assets/UI/SliderHandle.png", [this.mMaxPos, position[1]], [30,60]);
    // default Handle color = lighter blue
    this.mHandle.setColor([0,0.5,1,1]);
    
    // for allowing value of slider to be seen
    // default is show
    this.mTextVisible = true;
    this.mToFixedValue = 2;
    this.mText = new UIText(this.mMaxValue.toFixed(this.mToFixedValue).toString(), 
                            position, 
                            3, 
                            UIText.eHAlignment.eCenter, 
                            UIText.eVAlignment.eCenter,
                            [0, 0, 0, 1]);
    var pos = this.getUIXform().getXPos() + (this.getUIXform().getSize()[0]/2) + 75;
    this.mText.getUIXform().setXPos(pos);
    
    // for allowing slider to snap to values
    // default is off
    this.mSnap = false;
    this.mSnapBy = 1;
    
    // overrides/changes for if the bar is vertical
    if(this.mVertical) {
        this.mMaxPos = position[1] + (size[1]/2);
        this.mMinPos = position[1] - (size[1]/2);
        
        // for some reason, cannot just rotate existing handle
        this.setHandleTexture("assets/UI/VSliderHandle.png");
        this.mHandle.getUIXform().setPosition(position[0], this.mMaxPos);
        this.mHandle.getUIXform().setSize(60, 30);
        
        var pos = this.getUIXform().getYPos() + (this.getUIXform().getSize()[1]/2) + 50;
        this.mText.getUIXform().setPosition(position[0], pos);
    }
    
    // so can pretend value is greater than it is
    // (eg. can do volume slider that looks like it goes to 100 but only acutally goes to 1)
    // NOTE: this is only used for the text when displaying the value!!!
    this.mMultiplier = 1;
};
gEngine.Core.inheritPrototype(UISlider, UIBar);

/**
 * Draws the UISlider
 * @param {Camera} aCamera The camera to draw it on
 * @memberOf UISlider
 */
UISlider.prototype.draw = function(aCamera) {
    if(this.mVisible) {
        UIBar.prototype.draw.call(this, aCamera);
        this.mHandle.draw(aCamera);
        if(this.mTextVisible)
        {
            this.mText.draw(aCamera);
        }
    }
};

/**
 * Update the UISlider
 * @memberOf UISlider
 */
UISlider.prototype.update = function() {
    // get position of the mouse
    var mousePos = vec2.fromValues(gEngine.Input.getMousePosX(),
                                gEngine.Input.getMousePosY());
                                
    // check if the mouse is over the handle
    var mouseOver = this.mHandle.getUIBBox().containsPoint(mousePos[0], mousePos[1]);      
    
    // check for mouse click
    if(gEngine.Input.isButtonClicked(gEngine.Input.mouseButton.Left)){
        if(mouseOver){
            this.mClick = true;
        }
    }
    
    if(gEngine.Input.isButtonReleased(gEngine.Input.mouseButton.Left)){
        this.mClick = false;
    }
    
    // to do only if the handle is being moved
    if(this.mClick) {
        var barSize = this.mMaxPos - this.mMinPos;
        
        var pos = this.mVertical ? this._checkPosOnBar(mousePos[1]) : this._checkPosOnBar(mousePos[0]);
        
        // compute value based on handle's position
        var handlePosValue = (pos - this.mMinPos)/barSize * this.mMaxValue;
        
        // if snapping, compute the xpos to the nearest value to snap to
        // https://stackoverflow.com/questions/3254047/round-number-up-to-the-nearest-multiple-of-3
        if(this.mSnap) {
            var remainder = handlePosValue%this.mSnapBy;
            if(remainder < (this.mSnapBy/2)) {
                handlePosValue -= remainder;
            }
            else if(remainder > (this.mSnapBy/2)) {
                handlePosValue = handlePosValue + this.mSnapBy - remainder;
            }
            pos = ((handlePosValue/this.mMaxValue) * barSize) + this.mMinPos;
            pos = this._checkPosOnBar(pos);
        }
        
        
        // set handle's position
        if(this.mVertical) {
            this.mHandle.getUIXform().setYPos(pos);
        }
        else {
            this.mHandle.getUIXform().setXPos(pos);
        }
        
        // set the value
        this.setCurrentValue(handlePosValue);
    }
    
    // done here so from beginning will be shown properly
    this.mText.setText((this.mCurValue*this.mMultiplier).toFixed(this.mToFixedValue).toString());
    
    UIBar.prototype.update.call(this);
};

/**
 * Sets the Texture of the Handle
 * @param {String} tex Location of the Texture to be used for the Handle
 * @memberOf UISlider
 */
UISlider.prototype.setHandleTexture = function (tex) {
    this.mHandle.setTexture(tex);
};

/**
 * Sets the size of the Handle
 * @param {float} w Desired width for the Handle
 * @param {float} h Desired height for the Handle
 * @memberOf UISlider
 */
UISlider.prototype.setHandleSize = function(w, h) {
    this.mHandle.getUIXform().setSize(w, h);
};

/**
 * Sets the Color of the Handle
 * @param {float[]} c The Color to be used for the Handle
 * @memberOf UISlider
 */
UISlider.prototype.setHandleColor = function (c) {
    this.mHandle.setColor(c);
};

/**
 * Sets the Color of the Slider's Bar
 * @param {float[]} c The Color to be used for the Bar
 * @memberOf UISlider
 */
UISlider.prototype.setColor = function(c) {
    UIBar.prototype.setTopElemColor.call(this, c);
};

/**
 * Sets the X offset of the Text from position of Slider
 * @param {float} v The value to offset the Text by
 * @memberOf UISlider
 */
UISlider.prototype.setTextXOffset = function(v) {
    this.mText.getUIXform().setXPos(v);
};

/**
 * Sets the Color of the Text
 * @param {float[]} c The desired Color of the Text
 * @memberOf UISlider
 */
UISlider.prototype.setTextColor = function(c) {
    this.mText.setColor(c);
};

/**
 * Sets the size of the Text
 * @param {float} val The desired  of the Text
 * @memberOf UISlider
 */
UISlider.prototype.setTextSize = function(val) {
    this.mText.setTextHeight(val);
};

/**
 * Sets whether the Text should be visible
 * @param {bool} b Should the Text be shown
 * @memberOf UISlider
 */
UISlider.prototype.setTextVisible = function(b) {
    this.mTextVisible = b;
};

/**
 * Sets the amount of decimal places should be shown by the Text element
 * @param {float} v The amount of decimal places to be shown
 * @memberOf UISlider
 */
UISlider.prototype.setToFixedValue = function(v) {
    this.mToFixedValue = v;
};

/**
 * Sets whtether the Slider should snap
 * @param {bool} b Should the Slider snap to certain values
 * @memberOf UISlider
 */
UISlider.prototype.setSnap = function(b) {
    this.mSnap = b;
};

/**
 * Sets increments for Slider to snap by
 * @param {float} v The increments by which the Slider should snap by
 *                  (eg. if set to 0.5, Slider will snap to nearest 0.5
 *                      so 1.2 would snap to 1 and 1.7 would snap to 1.5) 
 * @memberOf UISlider
 */
UISlider.prototype.setSnapBy = function(v) {
    this.mSnapBy = v;
};

/**
 * Sets the current value of the Slider
 * @param {float} v The value the Slider should be at
 * @memberOf UISlider
 */
UISlider.prototype.setCurrentValue = function(v) {
    // call parent class's function
    UIBar.prototype.setCurrentValue.call(this, v);
    
    // set handle's position accordingly
    if(this.mVertical) {
        this.mHandle.getUIXform().setYPos(((v/this.mMaxValue)*(this.mMaxPos-this.mMinPos))+this.mMinPos);
    }
    else {
        this.mHandle.getUIXform().setXPos(((v/this.mMaxValue)*(this.mMaxPos-this.mMinPos))+this.mMinPos);
    }
};

/**
 * Sets the value of the multiplier
 * @param {float} v The number the current value should be multiplied by when shown
 * @memberOf UISlider
 */
UISlider.prototype.setMultiplier = function(v) {
    this.mMultiplier = v;
};

/**
 * Private function to return valid position on Slider if given position is not on Slider's Bar
 * @param {float} pos Position to check
 * @returns {float} Valid position on Slider
 */
UISlider.prototype._checkPosOnBar = function(pos) {
    if(pos < this.mMinPos) {
        return this.mMinPos;
    }
    if(pos > this.mMaxPos) {
        return this.mMaxPos;
    }
    return pos;
};