diff --git a/src/main/java/net/minecraftforge/client/gui/widget/ForgeSlider.java b/src/main/java/net/minecraftforge/client/gui/widget/ForgeSlider.java new file mode 100644 index 0000000000..1fdf1a1d54 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/gui/widget/ForgeSlider.java @@ -0,0 +1,220 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.client.gui.widget; + +import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.util.Mth; +import org.lwjgl.glfw.GLFW; + +import java.text.DecimalFormat; + +/** + * Slider widget implementation which allows inputting values in a certain range with optional step size. + */ +public class ForgeSlider extends AbstractSliderButton +{ + protected Component prefix; + protected Component suffix; + + protected double minValue; + protected double maxValue; + + /** Allows input of discontinuous values with a certain step */ + protected double stepSize; + + protected boolean drawString; + + private final DecimalFormat format; + + /** + * @param x x position of upper left corner + * @param y y position of upper left corner + * @param width Width of the widget + * @param height Height of the widget + * @param prefix {@link Component} displayed before the value string + * @param suffix {@link Component} displayed after the value string + * @param minValue Minimum (left) value of slider + * @param maxValue Maximum (right) value of slider + * @param currentValue Starting value when widget is first displayed + * @param stepSize Size of step used. Precision will automatically be calculated based on this value if this value is not 0. + * @param precision Only used when {@code stepSize} is 0. Limited to a maximum of 4 (inclusive). + * @param drawString Should text be displayed on the widget + */ + public ForgeSlider(int x, int y, int width, int height, Component prefix, Component suffix, double minValue, double maxValue, double currentValue, double stepSize, int precision, boolean drawString) + { + super(x, y, width, height, TextComponent.EMPTY, 0D); + this.prefix = prefix; + this.suffix = suffix; + this.minValue = minValue; + this.maxValue = maxValue; + this.stepSize = Math.abs(stepSize); + this.value = this.snapToNearest((currentValue - minValue) / (maxValue - minValue)); + this.drawString = drawString; + + if (stepSize == 0D) + { + precision = Math.min(precision, 4); + + StringBuilder builder = new StringBuilder("0"); + + if (precision > 0) + builder.append('.'); + + while (precision-- > 0) + builder.append('0'); + + this.format = new DecimalFormat(builder.toString()); + } + else if (Mth.equal(this.stepSize, Math.floor(this.stepSize))) + { + this.format = new DecimalFormat("0"); + } + else + { + this.format = new DecimalFormat(Double.toString(this.stepSize).replaceAll("\\d", "0")); + } + + this.updateMessage(); + } + + /** + * Overload with {@code stepSize} set to 1, useful for sliders with whole number values. + */ + public ForgeSlider(int x, int y, int width, int height, Component prefix, Component suffix, double minValue, double maxValue, double currentValue, boolean drawString) + { + this(x, y, width, height, prefix, suffix, minValue, maxValue, currentValue, 1D, 0, drawString); + } + + /** + * @return Current slider value as a double + */ + public double getValue() + { + return this.value * (maxValue - minValue) + minValue; + } + + /** + * @return Current slider value as an long + */ + public long getValueLong() + { + return Math.round(this.getValue()); + } + + /** + * @return Current slider value as an int + */ + public int getValueInt() + { + return (int) this.getValueLong(); + } + + /** + * @param value The new slider value + */ + public void setValue(double value) + { + this.value = this.snapToNearest((value - this.minValue) / (this.maxValue - this.minValue)); + this.updateMessage(); + } + + public String getValueString() + { + return this.format.format(this.getValue()); + } + + @Override + public void onClick(double mouseX, double mouseY) + { + this.setValueFromMouse(mouseX); + } + + @Override + protected void onDrag(double mouseX, double mouseY, double dragX, double dragY) + { + super.onDrag(mouseX, mouseY, dragX, dragY); + this.setValueFromMouse(mouseX); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) + { + boolean flag = keyCode == GLFW.GLFW_KEY_LEFT; + if (flag || keyCode == GLFW.GLFW_KEY_RIGHT) + { + if (this.minValue > this.maxValue) + flag = !flag; + float f = flag ? -1F : 1F; + if (stepSize <= 0D) + this.setSliderValue(this.value + (f / (this.width - 8))); + else + this.setValue(this.getValue() + f * this.stepSize); + } + + return false; + } + + private void setValueFromMouse(double mouseX) + { + this.setSliderValue((mouseX - (this.x + 4)) / (this.width - 8)); + } + + /** + * @param value Percentage of slider range + */ + private void setSliderValue(double value) + { + double oldValue = this.value; + this.value = this.snapToNearest(value); + if (!Mth.equal(oldValue, this.value)) + this.applyValue(); + + this.updateMessage(); + } + + /** + * Snaps the value, so that the displayed value is the nearest multiple of {@code stepSize}. + * If {@code stepSize} is 0, no snapping occurs. + */ + private double snapToNearest(double value) + { + if(stepSize <= 0D) + return Mth.clamp(value, 0D, 1D); + + value = Mth.lerp(Mth.clamp(value, 0D, 1D), this.minValue, this.maxValue); + + value = (stepSize * Math.round(value / stepSize)); + + if (this.minValue > this.maxValue) + { + value = Mth.clamp(value, this.maxValue, this.minValue); + } + else + { + value = Mth.clamp(value, this.minValue, this.maxValue); + } + + return Mth.map(value, this.minValue, this.maxValue, 0D, 1D); + } + + @Override + protected void updateMessage() + { + if (this.drawString) + { + this.setMessage(new TextComponent("").append(prefix).append(this.getValueString()).append(suffix)); + } + else + { + this.setMessage(TextComponent.EMPTY); + } + } + + @Override + protected void applyValue() {} +} diff --git a/src/main/java/net/minecraftforge/client/gui/widget/Slider.java b/src/main/java/net/minecraftforge/client/gui/widget/Slider.java index 3a95b4523f..6992e60d34 100644 --- a/src/main/java/net/minecraftforge/client/gui/widget/Slider.java +++ b/src/main/java/net/minecraftforge/client/gui/widget/Slider.java @@ -16,8 +16,13 @@ import javax.annotation.Nullable; /** * This class is blatantly stolen from iChunUtils with permission. * + * @deprecated This class has a few issues, + * mainly MinecraftForge/MinecraftForge#8485. + * Use {@link ForgeSlider} instead. + * * @author iChun */ +@Deprecated(since = "1.18.2", forRemoval = true) public class Slider extends ExtendedButton { /** The value of this slider control. */