com.badlogic.gdx.scenes.scene2d.ui.ProgressBar.java Source code

Java tutorial

Introduction

Here is the source code for com.badlogic.gdx.scenes.scene2d.ui.ProgressBar.java

Source

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.scenes.scene2d.ui;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.NinePatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.ChangeEvent;
import com.badlogic.gdx.scenes.scene2d.utils.Disableable;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.utils.Pools;

/** A progress bar is a widget that visually displays the progress of some activity or a value within given range. The progress bar
 * has a range (min, max) and a stepping between each value it represents. The percentage of completeness typically starts out as
 * an empty progress bar and gradually becomes filled in as the task or variable value progresses.
 * <p>
 * {@link ChangeEvent} is fired when the progress bar knob is moved. Cancelling the event will move the knob to where it was
 * previously.
 * <p>
 * The preferred height of a progress bar is determined by the larger of the knob and background. The preferred width of progress
 * bar is 140, a relatively arbitrary size.
 * @author mzechner
 * @author Nathan Sweet */
public class ProgressBar extends Widget implements Disableable {
    private ProgressBarStyle style;
    private float min, max, stepSize;
    private float value, animateFromValue;
    float position;
    final boolean vertical;
    private float animateDuration, animateTime;
    private Interpolation animateInterpolation = Interpolation.linear;
    private float[] snapValues;
    private float threshold;
    boolean disabled;
    boolean shiftIgnoresSnap;

    public ProgressBar(float min, float max, float stepSize, boolean vertical, Skin skin) {
        this(min, max, stepSize, vertical,
                skin.get("default-" + (vertical ? "vertical" : "horizontal"), ProgressBarStyle.class));
    }

    public ProgressBar(float min, float max, float stepSize, boolean vertical, Skin skin, String styleName) {
        this(min, max, stepSize, vertical, skin.get(styleName, ProgressBarStyle.class));
    }

    /** Creates a new progress bar. It's width is determined by the given prefWidth parameter, its height is determined by the
     * maximum of the height of either the progress bar {@link NinePatch} or progress bar handle {@link TextureRegion}. The min and
     * max values determine the range the values of this progress bar can take on, the stepSize parameter specifies the distance
     * between individual values.
     * <p>
     * E.g. min could be 4, max could be 10 and stepSize could be 0.2, giving you a total of 30 values, 4.0 4.2, 4.4 and so on.
     * @param min the minimum value
     * @param max the maximum value
     * @param stepSize the step size between values
     * @param style the {@link ProgressBarStyle} */
    public ProgressBar(float min, float max, float stepSize, boolean vertical, ProgressBarStyle style) {
        if (min > max)
            throw new IllegalArgumentException("max must be > min. min,max: " + min + ", " + max);
        if (stepSize <= 0)
            throw new IllegalArgumentException("stepSize must be > 0: " + stepSize);
        setStyle(style);
        this.min = min;
        this.max = max;
        this.stepSize = stepSize;
        this.vertical = vertical;
        this.value = min;
        setSize(getPrefWidth(), getPrefHeight());
    }

    public void setStyle(ProgressBarStyle style) {
        if (style == null)
            throw new IllegalArgumentException("style cannot be null.");
        this.style = style;
        invalidateHierarchy();
    }

    /** Returns the progress bar's style. Modifying the returned style may not have an effect until
     * {@link #setStyle(ProgressBarStyle)} is called. */
    public ProgressBarStyle getStyle() {
        return style;
    }

    @Override
    public void act(float delta) {
        super.act(delta);
        animateTime -= delta;
    }

    @Override
    public void draw(Batch batch, float parentAlpha) {
        ProgressBarStyle style = this.style;
        boolean disabled = this.disabled;
        final Drawable knob = (disabled && style.disabledKnob != null) ? style.disabledKnob : style.knob;
        final Drawable bg = (disabled && style.disabledBackground != null) ? style.disabledBackground
                : style.background;
        final Drawable knobBefore = (disabled && style.disabledKnobBefore != null) ? style.disabledKnobBefore
                : style.knobBefore;
        final Drawable knobAfter = (disabled && style.disabledKnobAfter != null) ? style.disabledKnobAfter
                : style.knobAfter;

        Color color = getColor();
        float x = getX();
        float y = getY();
        float width = getWidth();
        float height = getHeight();
        float knobHeight = knob == null ? 0 : knob.getMinHeight();
        float knobWidth = knob == null ? 0 : knob.getMinWidth();
        float value = getVisualValue();

        batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);

        if (vertical) {
            bg.draw(batch, x + (int) ((width - bg.getMinWidth()) * 0.5f), y, bg.getMinWidth(), height);

            float positionHeight = height - (bg.getTopHeight() + bg.getBottomHeight());
            float knobHeightHalf = 0;
            if (min != max) {
                if (knob == null) {
                    knobHeightHalf = knobBefore == null ? 0 : knobBefore.getMinHeight() * 0.5f;
                    position = (value - min) / (max - min) * (positionHeight - knobHeightHalf);
                    position = Math.min(positionHeight - knobHeightHalf, position);
                } else {
                    knobHeightHalf = knobHeight * 0.5f;
                    position = (value - min) / (max - min) * (positionHeight - knobHeight);
                    position = Math.min(positionHeight - knobHeight, position) + bg.getBottomHeight();
                }
                position = Math.max(0, position);
            }

            if (knobBefore != null) {
                float offset = 0;
                if (bg != null)
                    offset = bg.getTopHeight();
                knobBefore.draw(batch, x + (int) ((width - knobBefore.getMinWidth()) * 0.5f), y + offset,
                        knobBefore.getMinWidth(), (int) (position + knobHeightHalf));
            }
            if (knobAfter != null) {
                knobAfter.draw(batch, x + (int) ((width - knobAfter.getMinWidth()) * 0.5f),
                        y + (int) (position + knobHeightHalf), knobAfter.getMinWidth(),
                        height - (int) (position + knobHeightHalf));
            }
            if (knob != null)
                knob.draw(batch, x + (int) ((width - knobWidth) * 0.5f), (int) (y + position), knobWidth,
                        knobHeight);
        } else {
            bg.draw(batch, x, y + (int) ((height - bg.getMinHeight()) * 0.5f), width, bg.getMinHeight());

            float positionWidth = width - (bg.getLeftWidth() + bg.getRightWidth());
            float knobWidthHalf = 0;
            if (min != max) {
                if (knob == null) {
                    knobWidthHalf = knobBefore == null ? 0 : knobBefore.getMinWidth() * 0.5f;
                    position = (value - min) / (max - min) * (positionWidth - knobWidthHalf);
                    position = Math.min(positionWidth - knobWidthHalf, position);
                } else {
                    knobWidthHalf = knobWidth * 0.5f;
                    position = (value - min) / (max - min) * (positionWidth - knobWidth);
                    position = Math.min(positionWidth - knobWidth, position) + bg.getLeftWidth();
                }
                position = Math.max(0, position);
            }

            if (knobBefore != null) {
                float offset = 0;
                if (bg != null)
                    offset = bg.getLeftWidth();
                knobBefore.draw(batch, x + offset, y + (int) ((height - knobBefore.getMinHeight()) * 0.5f),
                        (int) (position + knobWidthHalf), knobBefore.getMinHeight());
            }
            if (knobAfter != null) {
                knobAfter.draw(batch, x + (int) (position + knobWidthHalf),
                        y + (int) ((height - knobAfter.getMinHeight()) * 0.5f),
                        width - (int) (position + knobWidthHalf), knobAfter.getMinHeight());
            }
            if (knob != null)
                knob.draw(batch, (int) (x + position), (int) (y + (height - knobHeight) * 0.5f), knobWidth,
                        knobHeight);
        }
    }

    public float getValue() {
        return value;
    }

    /** If {@link #setAnimateDuration(float) animating} the progress bar value, this returns the value current displayed. */
    public float getVisualValue() {
        if (animateTime > 0)
            return animateInterpolation.apply(animateFromValue, value, 1 - animateTime / animateDuration);
        return value;
    }

    /** Returns progress bar visual position within the range. */
    protected float getKnobPosition() {
        return this.position;
    }

    /** Sets the progress bar position, rounded to the nearest step size and clamped to the minimum and maximum values.
     * {@link #clamp(float)} can be overridden to allow values outside of the progress bar's min/max range.
     * @return false if the value was not changed because the progress bar already had the value or it was canceled by a listener. */
    public boolean setValue(float value) {
        value = clamp(Math.round(value / stepSize) * stepSize);
        if (!shiftIgnoresSnap
                || (!Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) && !Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT)))
            value = snap(value);
        float oldValue = this.value;
        if (value == oldValue)
            return false;
        float oldVisualValue = getVisualValue();
        this.value = value;
        ChangeEvent changeEvent = Pools.obtain(ChangeEvent.class);
        boolean cancelled = fire(changeEvent);
        if (cancelled)
            this.value = oldValue;
        else if (animateDuration > 0) {
            animateFromValue = oldVisualValue;
            animateTime = animateDuration;
        }
        Pools.free(changeEvent);
        return !cancelled;
    }

    /** Clamps the value to the progress bar's min/max range. This can be overridden to allow a range different from the progress
     * bar knob's range. */
    protected float clamp(float value) {
        return MathUtils.clamp(value, min, max);
    }

    /** Sets the range of this progress bar. The progress bar's current value is clamped to the range. */
    public void setRange(float min, float max) {
        if (min > max)
            throw new IllegalArgumentException("min must be <= max");
        this.min = min;
        this.max = max;
        if (value < min)
            setValue(min);
        else if (value > max)
            setValue(max);
    }

    public void setStepSize(float stepSize) {
        if (stepSize <= 0)
            throw new IllegalArgumentException("steps must be > 0: " + stepSize);
        this.stepSize = stepSize;
    }

    public float getPrefWidth() {
        if (vertical) {
            final Drawable knob = (disabled && style.disabledKnob != null) ? style.disabledKnob : style.knob;
            final Drawable bg = (disabled && style.disabledBackground != null) ? style.disabledBackground
                    : style.background;
            return Math.max(knob == null ? 0 : knob.getMinWidth(), bg.getMinWidth());
        } else
            return 140;
    }

    public float getPrefHeight() {
        if (vertical)
            return 140;
        else {
            final Drawable knob = (disabled && style.disabledKnob != null) ? style.disabledKnob : style.knob;
            final Drawable bg = (disabled && style.disabledBackground != null) ? style.disabledBackground
                    : style.background;
            return Math.max(knob == null ? 0 : knob.getMinHeight(), bg.getMinHeight());
        }
    }

    public float getMinValue() {
        return this.min;
    }

    public float getMaxValue() {
        return this.max;
    }

    public float getStepSize() {
        return this.stepSize;
    }

    /** If > 0, changes to the progress bar value via {@link #setValue(float)} will happen over this duration in seconds. */
    public void setAnimateDuration(float duration) {
        this.animateDuration = duration;
    }

    /** Sets the interpolation to use for {@link #setAnimateDuration(float)}. */
    public void setAnimateInterpolation(Interpolation animateInterpolation) {
        if (animateInterpolation == null)
            throw new IllegalArgumentException("animateInterpolation cannot be null.");
        this.animateInterpolation = animateInterpolation;
    }

    /** Will make this progress bar snap to the specified values, if the knob is within the threshold. */
    public void setSnapToValues(float[] values, float threshold) {
        this.snapValues = values;
        this.threshold = threshold;
    }

    /** Returns a snapped value. */
    private float snap(float value) {
        if (snapValues == null)
            return value;
        for (int i = 0; i < snapValues.length; i++) {
            if (Math.abs(value - snapValues[i]) <= threshold)
                return snapValues[i];
        }
        return value;
    }

    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    public boolean isDisabled() {
        return disabled;
    }

    /** The style for a progress bar, see {@link ProgressBar}.
     * @author mzechner
     * @author Nathan Sweet */
    static public class ProgressBarStyle {
        /** The progress bar background, stretched only in one direction. */
        public Drawable background;
        /** Optional. **/
        public Drawable disabledBackground;
        /** Optional, centered on the background. */
        public Drawable knob, disabledKnob;
        /** Optional. */
        public Drawable knobBefore, knobAfter, disabledKnobBefore, disabledKnobAfter;

        public ProgressBarStyle() {
        }

        public ProgressBarStyle(Drawable background, Drawable knob) {
            this.background = background;
            this.knob = knob;
        }

        public ProgressBarStyle(ProgressBarStyle style) {
            this.background = style.background;
            this.disabledBackground = style.disabledBackground;
            this.knob = style.knob;
            this.disabledKnob = style.disabledKnob;
            this.knobBefore = style.knobBefore;
            this.knobAfter = style.knobAfter;
            this.disabledKnobBefore = style.disabledKnobBefore;
            this.disabledKnobAfter = style.disabledKnobAfter;
        }
    }
}