Redesign the mod menu, settings and cosmetics screens #6
@@ -42,8 +42,8 @@ public class AnimationConfig {
|
||||
public final SelectProperty curve;
|
||||
|
||||
public AnimationConfig(boolean enabled, int delay, int stagger) {
|
||||
this.duration = Property.integer(delay);
|
||||
this.stagger = Property.integer(stagger);
|
||||
this.duration = Property.integer(delay).range(0, 2000, 50).unit("ms");
|
||||
this.stagger = Property.integer(stagger).range(0, 300, 10).unit("ms");
|
||||
this.curve = Property.select(1, "Ease In Out Cubic", "Ease Out Cubic", "Ease In Out Back");
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ public class Theme {
|
||||
public static final ColorProperty SCROLL = Property.color(0xFFE2BF7D);
|
||||
|
||||
// UI metrics
|
||||
public static final IntProperty BG_RADIUS = Property.integer(12);
|
||||
public static final IntProperty WIDGET_RADIUS = Property.integer(10);
|
||||
public static final IntProperty BG_RADIUS = Property.integer(12).range(0, 24);
|
||||
public static final IntProperty WIDGET_RADIUS = Property.integer(10).range(0, 20);
|
||||
|
||||
// Font
|
||||
public static final SelectProperty FONT = Property.font(2);
|
||||
|
||||
@@ -7,11 +7,36 @@ public class FloatProperty extends Property {
|
||||
public float value;
|
||||
public final float defaultValue;
|
||||
|
||||
/** Bounds for a slider, when the setting has a sensible range */
|
||||
public Float min, max;
|
||||
public float step = 0.1f;
|
||||
/** Shown after the value, like "×" */
|
||||
public String unit = "";
|
||||
|
||||
public FloatProperty(float defaultValue) {
|
||||
this.value = defaultValue;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the setting a range, so it's edited with a slider.
|
||||
*/
|
||||
public FloatProperty range(float min, float max, float step) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FloatProperty unit(String unit) {
|
||||
this.unit = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasRange() {
|
||||
return min != null && max != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.value = defaultValue;
|
||||
|
||||
@@ -7,11 +7,40 @@ public class IntProperty extends Property {
|
||||
public int value;
|
||||
public final int defaultValue;
|
||||
|
||||
/** Bounds for a slider, when the setting has a sensible range */
|
||||
public Integer min, max;
|
||||
public int step = 1;
|
||||
/** Shown after the value, like "ms" */
|
||||
public String unit = "";
|
||||
|
||||
public IntProperty(int defaultValue) {
|
||||
this.value = defaultValue;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the setting a range, so it's edited with a slider.
|
||||
*/
|
||||
public IntProperty range(int min, int max) {
|
||||
return range(min, max, 1);
|
||||
}
|
||||
|
||||
public IntProperty range(int min, int max, int step) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntProperty unit(String unit) {
|
||||
this.unit = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasRange() {
|
||||
return min != null && max != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.value = defaultValue;
|
||||
|
||||
@@ -13,10 +13,10 @@ import org.saturnclient.config.property.NamedProperty;
|
||||
public class ModLayout {
|
||||
public IntProperty x = Property.integer(0);
|
||||
public IntProperty y = Property.integer(0);
|
||||
public FloatProperty scale = Property.floatProp(1.0f);
|
||||
public FloatProperty scale = Property.floatProp(1.0f).range(0.5f, 4f, 0.1f).unit("×");
|
||||
public ColorProperty bgColor = Property.color(0x00000000);
|
||||
public ColorProperty fgColor = Property.color(0xFFffffff);
|
||||
public IntProperty radius = Property.integer(0);
|
||||
public IntProperty radius = Property.integer(0).range(0, 20);
|
||||
public SelectProperty font = Property.font(0);
|
||||
public boolean renderBackground = true;
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ public class FreelookMod extends Mod {
|
||||
private static final BoolProperty toggle = Property.bool(true);
|
||||
private static final KeybindingProperty freelookKey = Property.keybinding(GLFWProvider.GLFW_KEY_H);
|
||||
|
||||
public static final FloatProperty zoomLevel = Property.floatProp(2.0f);
|
||||
public static final IntProperty zoomInDuration = Property.integer(500);
|
||||
public static final IntProperty zoomOutDuration = Property.integer(300);
|
||||
public static final FloatProperty zoomLevel = Property.floatProp(2.0f).range(1f, 5f, 0.1f).unit("×");
|
||||
public static final IntProperty zoomInDuration = Property.integer(500).range(0, 2000, 50).unit("ms");
|
||||
public static final IntProperty zoomOutDuration = Property.integer(300).range(0, 2000, 50).unit("ms");
|
||||
public static final SelectProperty curve = Property.select(1, "Ease In Out Cubic", "Ease Out Cubic", "Ease In Out Back");
|
||||
|
||||
// --- State Variables ---
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.saturnclient.mod.ModSpec;
|
||||
public class FullbrightMod extends Mod {
|
||||
|
||||
private static final BoolProperty enabled = Property.bool(false);
|
||||
private static final IntProperty brightness = Property.integer(100);
|
||||
private static final IntProperty brightness = Property.integer(100).range(0, 1000, 10);
|
||||
|
||||
public FullbrightMod() {
|
||||
super(
|
||||
|
||||
@@ -18,9 +18,9 @@ public class ZoomMod extends Mod {
|
||||
private static final BoolProperty enabled = Property.bool(false);
|
||||
private static final BoolProperty toggle = Property.bool(false);
|
||||
public static final KeybindingProperty zoomKey = Property.keybinding(GLFWProvider.GLFW_KEY_C);
|
||||
public static final FloatProperty zoomLevel = Property.floatProp(3.0f);
|
||||
public static final IntProperty zoomInDuration = Property.integer(700);
|
||||
public static final IntProperty zoomOutDuration = Property.integer(300);
|
||||
public static final FloatProperty zoomLevel = Property.floatProp(3.0f).range(1f, 10f, 0.5f).unit("×");
|
||||
public static final IntProperty zoomInDuration = Property.integer(700).range(0, 2000, 50).unit("ms");
|
||||
public static final IntProperty zoomOutDuration = Property.integer(300).range(0, 2000, 50).unit("ms");
|
||||
public static final SelectProperty curve = Property.select(1, "Ease In Out Cubic", "Ease Out Cubic",
|
||||
"Ease In Out Back");
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.saturnclient.ui.components.inputs.FloatInput;
|
||||
import org.saturnclient.ui.components.inputs.IntInput;
|
||||
import org.saturnclient.ui.components.inputs.KeybindingSelector;
|
||||
import org.saturnclient.ui.components.inputs.Select;
|
||||
import org.saturnclient.ui.components.inputs.Slider;
|
||||
import org.saturnclient.ui.components.inputs.Toggle;
|
||||
import org.saturnclient.ui.elements.Label;
|
||||
import org.saturnclient.ui.elements.Scroll;
|
||||
@@ -104,9 +105,9 @@ public final class PropertyList {
|
||||
} else if (prop instanceof ColorProperty p) {
|
||||
return new ColorInput(p);
|
||||
} else if (prop instanceof IntProperty p) {
|
||||
return new IntInput(p);
|
||||
return p.hasRange() ? Slider.of(p) : new IntInput(p);
|
||||
} else if (prop instanceof FloatProperty p) {
|
||||
return new FloatInput(p);
|
||||
return p.hasRange() ? Slider.of(p) : new FloatInput(p);
|
||||
} else if (prop instanceof SelectProperty p) {
|
||||
return new Select(p);
|
||||
} else if (prop instanceof KeybindingProperty p) {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Locale;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.function.DoubleSupplier;
|
||||
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.config.property.FloatProperty;
|
||||
import org.saturnclient.config.property.IntProperty;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
import org.saturnclient.ui.elements.Field;
|
||||
|
||||
/**
|
||||
* Edits a number within a range by clicking or dragging along a track, with
|
||||
* the value shown beside it.
|
||||
*/
|
||||
public class Slider extends Element {
|
||||
private static final int TRACK_HEIGHT = 6;
|
||||
private static final int KNOB = 16;
|
||||
private static final int VALUE_WIDTH = 72;
|
||||
private static final int VALUE_GAP = 14;
|
||||
|
||||
private final double min, max, step;
|
||||
private final int decimals;
|
||||
private final String unit;
|
||||
private final DoubleSupplier value;
|
||||
private final DoubleConsumer onChange;
|
||||
|
||||
private boolean dragging = false;
|
||||
|
||||
private Slider(double min, double max, double step, String unit, DoubleSupplier value, DoubleConsumer onChange) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.step = step;
|
||||
this.unit = unit;
|
||||
this.value = value;
|
||||
this.onChange = onChange;
|
||||
this.decimals = Math.max(0, BigDecimal.valueOf(step).stripTrailingZeros().scale());
|
||||
this.width = 280;
|
||||
this.height = Field.HEIGHT;
|
||||
}
|
||||
|
||||
public static Slider of(IntProperty prop) {
|
||||
return new Slider(prop.min, prop.max, prop.step, prop.unit, () -> prop.value,
|
||||
v -> prop.value = (int) Math.round(v));
|
||||
}
|
||||
|
||||
public static Slider of(FloatProperty prop) {
|
||||
return new Slider(prop.min, prop.max, prop.step, prop.unit, () -> prop.value,
|
||||
v -> prop.value = (float) v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
double current = value.getAsDouble();
|
||||
float progress = (float) Math.max(0, Math.min(1, (current - min) / (max - min)));
|
||||
int trackWidth = getTrackWidth();
|
||||
int trackY = (height - TRACK_HEIGHT) / 2;
|
||||
int fillWidth = (int) (trackWidth * progress);
|
||||
|
||||
renderScope.drawRoundedRectangle(KNOB / 2, trackY, trackWidth, TRACK_HEIGHT, TRACK_HEIGHT / 2,
|
||||
Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
|
||||
if (fillWidth > 0) {
|
||||
renderScope.drawRoundedRectangle(KNOB / 2, trackY, fillWidth, TRACK_HEIGHT, TRACK_HEIGHT / 2,
|
||||
Theme.ACCENT.value);
|
||||
}
|
||||
|
||||
boolean knobHovered = dragging || ctx.isHovering(fillWidth, 0, KNOB, height);
|
||||
renderScope.drawRoundedRectangle(fillWidth, (height - KNOB) / 2, KNOB, KNOB, KNOB / 2, 3,
|
||||
knobHovered ? Theme.ACCENT.value : Theme.FOREGROUND.value, Theme.BACKGROUND.value);
|
||||
|
||||
// Value
|
||||
int valueX = width - VALUE_WIDTH;
|
||||
renderScope.drawRoundedRectangle(valueX, 0, VALUE_WIDTH, height, Theme.WIDGET_RADIUS.value,
|
||||
Theme.withAlpha(0.07f, Theme.FOREGROUND.value));
|
||||
|
||||
renderScope.getMatrixStack().push();
|
||||
renderScope.getMatrixStack().translate(valueX, 0);
|
||||
Field.drawCenteredText(renderScope, format(current), VALUE_WIDTH, height, Theme.FONT.value,
|
||||
Theme.FOREGROUND.value);
|
||||
renderScope.getMatrixStack().pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
dragging = mouseX >= 0 && mouseX <= getTrackWidth() + KNOB && mouseY >= 0 && mouseY <= height;
|
||||
|
||||
if (dragging) {
|
||||
setFromMouse(mouseX);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
if (dragging) {
|
||||
setFromMouse(mouseX);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(double mouseX, double mouseY, int button) {
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
private void setFromMouse(double mouseX) {
|
||||
double progress = Math.max(0, Math.min(1, (mouseX - KNOB / 2.0) / getTrackWidth()));
|
||||
double raw = min + progress * (max - min);
|
||||
double snapped = min + Math.round((raw - min) / step) * step;
|
||||
|
||||
// Snapping in floating point can leave 2.9999999, so round to the step's decimals.
|
||||
double rounded = BigDecimal.valueOf(snapped).setScale(decimals, RoundingMode.HALF_UP).doubleValue();
|
||||
onChange.accept(Math.max(min, Math.min(max, rounded)));
|
||||
}
|
||||
|
||||
private String format(double current) {
|
||||
String number = String.format(Locale.ROOT, "%." + decimals + "f", current);
|
||||
|
||||
if (unit.isEmpty()) {
|
||||
return number;
|
||||
}
|
||||
|
||||
// Word units get a space ("700 ms"); symbols don't ("3.0×").
|
||||
return Character.isLetter(unit.charAt(0)) ? number + " " + unit : number + unit;
|
||||
}
|
||||
|
||||
private int getTrackWidth() {
|
||||
return width - VALUE_WIDTH - VALUE_GAP - KNOB;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user