![JAR search and dependency download from the Maven repository](/logo.png)
pl.kotcrab.vis.ui.widget.VisProgressBar Maven / Gradle / Ivy
Show all versions of vis-ui Show documentation
/*******************************************************************************
* Copyright 2014 Pawel Pastuszak
*
* 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 pl.kotcrab.vis.ui.widget;
import pl.kotcrab.vis.ui.VisUI;
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.ui.ProgressBar.ProgressBarStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Widget;
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.
*
* {@link ChangeEvent} is fired when the progress bar knob is moved. Cancelling the event will move the knob to where it was
* previously.
*
* 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
* @author Pawel Pastuszak */
public class VisProgressBar extends Widget implements Disableable {
// This class was copied from LibGDX, few lines were changed.
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 VisProgressBar (float min, float max, float stepSize, boolean vertical) {
this(min, max, stepSize, vertical, VisUI.skin.get("default-" + (vertical ? "vertical" : "horizontal"),
ProgressBarStyle.class));
}
public VisProgressBar (float min, float max, float stepSize, boolean vertical, String styleName) {
this(min, max, stepSize, vertical, VisUI.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.
*
* 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 VisProgressBar (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;
}
@Override
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;
}
@Override
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;
}
@Override
public void setDisabled (boolean disabled) {
this.disabled = disabled;
}
public boolean isDisabled () {
return disabled;
}
}