Skip to content

Commit

Permalink
Add modern implementation of the Slider widget and deprecate the old …
Browse files Browse the repository at this point in the history
  • Loading branch information
PlatinPython authored Apr 13, 2022
1 parent eed24f0 commit f0b90dc
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 0 deletions.
220 changes: 220 additions & 0 deletions src/main/java/net/minecraftforge/client/gui/widget/ForgeSlider.java
Original file line number Diff line number Diff line change
@@ -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() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
/**
* This class is blatantly stolen from iChunUtils with permission.
*
* @deprecated This class has a few issues,
* mainly <a href="https://github.com/MinecraftForge/MinecraftForge/issues/8485">MinecraftForge/MinecraftForge#8485</a>.
* Use {@link ForgeSlider} instead.
*
* @author iChun
*/
@Deprecated(since = "1.18.2", forRemoval = true)
public class Slider extends ExtendedButton
{
/** The value of this slider control. */
Expand Down

0 comments on commit f0b90dc

Please sign in to comment.