Redesign the mod menu, settings and cosmetics screens #6

Merged
selimaj-dev merged 13 commits from ui-redesign into master 2026-09-27 16:27:35 +00:00
18 changed files with 111 additions and 68 deletions
Showing only changes of commit 33c09b6922 - Show all commits
@@ -49,8 +49,8 @@ public class AnimationConfig {
public void init(String name) { public void init(String name) {
// config.property("Enable " + name + " Animations", this.enabled); // config.property("Enable " + name + " Animations", this.enabled);
config.property(name + " Duration", this.duration); config.property(name + " Duration", this.duration, "How long the animation takes");
config.property(name + " Stagger", this.stagger); config.property(name + " Stagger", this.stagger, "Delay between items appearing one after another");
config.property(name + " Curve", this.curve); config.property(name + " Curve", this.curve, "Easing of the animation");
} }
} }
@@ -27,11 +27,11 @@ public class Config {
File configFile = new File(Providers.saturn.getClient().getRunDirectory(), "saturn.json"); File configFile = new File(Providers.saturn.getClient().getRunDirectory(), "saturn.json");
config = new ConfigManager(configFile, "Saturn Client"); config = new ConfigManager(configFile, "Saturn Client");
config.property("Realistic logo", realisticLogo); config.property("Realistic logo", realisticLogo, "Use the realistic Saturn logo");
config.property("Saturn client title screen", saturnTitleScreen); config.property("Saturn client title screen", saturnTitleScreen, "Replace the title screen with Saturn's");
config.property("Open Emote Wheel", openEmoteWheel); config.property("Open Emote Wheel", openEmoteWheel, "The key that opens the emote wheel");
config.property("Cloak Physics", cloakPhysics); config.property("Cloak Physics", cloakPhysics, "Let cloaks swing as you move");
config.property("Stagger Animations", stagger); config.property("Stagger Animations", stagger, "Animate list items one after another");
// Initialize a sub namespace for theme // Initialize a sub namespace for theme
Theme.init(config); Theme.init(config);
@@ -70,6 +70,14 @@ public class ConfigManager {
return value; return value;
} }
/**
* Add a property with the line shown under its name
*/
public Property property(String name, Property value, String description) {
value.description = description;
return property(name, value);
}
/** /**
* Add empty namespace * Add empty namespace
*/ */
@@ -69,18 +69,18 @@ public class Theme {
public static void init(ConfigManager parent) { public static void init(ConfigManager parent) {
config = new ConfigManager(parent, "UI Style"); config = new ConfigManager(parent, "UI Style");
config.property("Background", BACKGROUND); config.property("Background", BACKGROUND, "Panels and screens");
config.property("Foreground", FOREGROUND); config.property("Foreground", FOREGROUND, "Text");
config.property("Primary", PRIMARY); config.property("Primary", PRIMARY, "Buttons and cards");
config.property("Primary Foreground", PRIMARY_FG); config.property("Primary Foreground", PRIMARY_FG, "Text on buttons and cards");
config.property("Accent", ACCENT); config.property("Accent", ACCENT, "Selected and enabled items");
config.property("Accent Foreground", ACCENT_FG); config.property("Accent Foreground", ACCENT_FG, "Text on the accent color");
config.property("Scrollbar Color", SCROLL); config.property("Scrollbar Color", SCROLL, "The scrollbar's handle");
config.property("Background Radius", BG_RADIUS); config.property("Background Radius", BG_RADIUS, "Corner rounding of panels");
config.property("Widget Radius", WIDGET_RADIUS); config.property("Widget Radius", WIDGET_RADIUS, "Corner rounding of buttons and cards");
config.property("Font", FONT); config.property("Font", FONT, "Font of every Saturn screen");
} }
/** /**
@@ -8,4 +8,12 @@ public class NamedProperty {
this.name = name; this.name = name;
this.prop = value; this.prop = value;
} }
/**
* Sets the line shown under the setting's name.
*/
public NamedProperty description(String description) {
this.prop.description = description;
return this;
}
} }
@@ -7,6 +7,9 @@ import java.util.Map;
public abstract class Property { public abstract class Property {
public boolean isReset = false; public boolean isReset = false;
/** One line under the setting's name saying what it does, or "" */
public String description = "";
// ---------- Factory Methods ---------- // ---------- Factory Methods ----------
public static NamespaceProperty namespace(Map<String, Property> value) { public static NamespaceProperty namespace(Map<String, Property> value) {
@@ -26,17 +26,22 @@ public class ModLayout {
public NamedProperty prop() { public NamedProperty prop() {
// A LinkedHashMap, unlike Map.of, keeps the order the settings are shown in. // A LinkedHashMap, unlike Map.of, keeps the order the settings are shown in.
Map<String, Property> props = new LinkedHashMap<>(); Map<String, Property> props = new LinkedHashMap<>();
props.put("X", x); put(props, "X", x, "Distance from the left of the screen");
props.put("Y", y); put(props, "Y", y, "Distance from the top of the screen");
props.put("Scale", scale); put(props, "Scale", scale, "Size of the display");
props.put("Background Color", bgColor); put(props, "Background Color", bgColor, "Fill behind the display");
props.put("Foreground Color", fgColor); put(props, "Foreground Color", fgColor, "Color of the text");
props.put("Corner Radius", radius); put(props, "Corner Radius", radius, "Rounding of the background's corners");
props.put("Font", font); put(props, "Font", font, "Font of the text");
return Property.namespace(props).named("In-Game Display"); return Property.namespace(props).named("In-Game Display");
} }
private static void put(Map<String, Property> props, String name, Property prop, String description) {
prop.description = description;
props.put(name, prop);
}
public ModLayout() { public ModLayout() {
} }
@@ -32,7 +32,7 @@ public class ArmorDisplayMod extends Mod implements HudMod {
.tags("Utility") .tags("Utility")
.requires(Providers.feature::player), .requires(Providers.feature::player),
enabled.named("Enabled"), enabled.named("Enabled"),
useMainHand.named("Use Main Hand"), useMainHand.named("Use Main Hand").description("Also show the item in your main hand"),
layout.prop()); layout.prop());
} }
@@ -31,8 +31,8 @@ public class ClockMod extends Mod implements HudMod {
.version("v0.2.0") .version("v0.2.0")
.tags("Utility"), .tags("Utility"),
enabled.named("Enabled"), enabled.named("Enabled"),
format.named("Format"), format.named("Format").description("24-hour or 12-hour time"),
showSeconds.named("Show seconds"), showSeconds.named("Show seconds").description("Include seconds in the time"),
layout.prop()); layout.prop());
} }
@@ -33,7 +33,7 @@ public class CoordinatesMod extends Mod implements HudMod {
.tags("Utility") .tags("Utility")
.requires(Providers.feature::player), .requires(Providers.feature::player),
enabled.named("Enabled"), enabled.named("Enabled"),
displayMethod.named("Display Method"), displayMethod.named("Display Method").description("One line or one per axis, with or without labels"),
layout.prop()); layout.prop());
} }
@@ -43,12 +43,12 @@ public class FreelookMod extends Mod {
.version("v0.3.1") .version("v0.3.1")
.requires(Providers.feature::render), .requires(Providers.feature::render),
enabled.named("Enabled"), enabled.named("Enabled"),
toggle.named("Toggle freelook"), toggle.named("Toggle freelook").description("Press the key once instead of holding it"),
freelookKey.named("Freelook Keybinding"), freelookKey.named("Freelook Keybinding").description("The key that starts freelook"),
zoomLevel.named("Zoom Level"), zoomLevel.named("Zoom Level").description("Zoom when freelook starts in first person"),
zoomInDuration.named("Zoom In Duration"), zoomInDuration.named("Zoom In Duration").description("How long that zoom takes to ease in"),
zoomOutDuration.named("Zoom Out Duration"), zoomOutDuration.named("Zoom Out Duration").description("How long that zoom takes to ease out"),
curve.named("Curve")); curve.named("Curve").description("Easing of the zoom"));
} }
// --- Lifecycle Methods --- // --- Lifecycle Methods ---
@@ -28,7 +28,7 @@ public class FullbrightMod extends Mod {
.tags("Camera") .tags("Camera")
.version("v0.2.0"), .version("v0.2.0"),
enabled.named("Enabled"), enabled.named("Enabled"),
brightness.named("Brightness %")); brightness.named("Brightness %").description("How bright the world gets"));
} }
// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -31,8 +31,8 @@ public class HealthDisplayMod extends Mod implements HudMod {
.tags("Utility") .tags("Utility")
.requires(Providers.feature::player), .requires(Providers.feature::player),
enabled.named("Enabled"), enabled.named("Enabled"),
displayMode.named("Display mode"), displayMode.named("Display mode").description("Show health points or hearts"),
decimals.named("Decimals"), decimals.named("Decimals").description("Decimal places shown"),
layout.prop()); layout.prop());
} }
@@ -54,11 +54,11 @@ public class KeystrokesMod extends Mod implements HudMod {
.tags("Utility") .tags("Utility")
.requires(Providers.feature::player), .requires(Providers.feature::player),
enabled.named("Enabled"), enabled.named("Enabled"),
showMouse.named("Show mouse clicks"), showMouse.named("Show mouse clicks").description("Show the left and right mouse buttons"),
showSpace.named("Show space"), showSpace.named("Show space").description("Show the space bar"),
layout.prop(), layout.prop(),
clickFg.named("Clicked fg"), clickFg.named("Clicked fg").description("Text color of a pressed key"),
clickBg.named("Clicked bg")); clickBg.named("Clicked bg").description("Background of a pressed key"));
layout.renderBackground = false; layout.renderBackground = false;
} }
@@ -58,16 +58,16 @@ public class NametagsMod extends Mod {
.tags("Camera", "Utility") .tags("Camera", "Utility")
.version("v0.2.0"), .version("v0.2.0"),
enabled.named("Enabled"), enabled.named("Enabled"),
healthDisplay.named("Display health"), healthDisplay.named("Display health").description("Show health after the name"),
players.named("Players"), players.named("Players").description("Show health on players"),
hostile.named("Hostile"), hostile.named("Hostile").description("Show health on hostile mobs"),
passive.named("Passive"), passive.named("Passive").description("Show health on passive mobs"),
heartEmoji.named("Heart emoji"), heartEmoji.named("Heart emoji").description("Add a heart after the health"),
obfuscate.named("Obfuscate enemy names"), obfuscate.named("Obfuscate enemy names").description("Scramble names into unreadable text"),
unit.named("Unit"), unit.named("Unit").description("Show health points or hearts"),
format.named("Format"), format.named("Format").description("Value, value out of total, or percent"),
nameColor.named("Name color"), nameColor.named("Name color").description("Color of the name"),
healthColor.named("Health color")); healthColor.named("Health color").description("Color of the health"));
} }
// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -34,9 +34,9 @@ public class SpeedometerMod extends Mod implements HudMod {
.tags("Utility") .tags("Utility")
.requires(Providers.feature::player), .requires(Providers.feature::player),
enabled.named("Enabled"), enabled.named("Enabled"),
axis.named("Speed type"), axis.named("Speed type").description("All movement, only horizontal or only vertical"),
unitText.named("Unit text"), unitText.named("Unit text").description("Unit shown after the speed"),
showLabel.named("Show speed label"), showLabel.named("Show speed label").description("Show “Speed:” before the value"),
layout.prop()); layout.prop());
} }
@@ -31,12 +31,12 @@ public class ZoomMod extends Mod {
.tags("Camera", "Utility") .tags("Camera", "Utility")
.version("v0.2.0"), .version("v0.2.0"),
enabled.named("Enabled"), enabled.named("Enabled"),
toggle.named("Toggle zoom"), toggle.named("Toggle zoom").description("Press the key once instead of holding it"),
zoomKey.named("Zoom Keybinding"), zoomKey.named("Zoom Keybinding").description("The key that zooms in"),
zoomLevel.named("Zoom Level"), zoomLevel.named("Zoom Level").description("How far the camera zooms in"),
zoomInDuration.named("Zoom In Duration"), zoomInDuration.named("Zoom In Duration").description("How long zooming in takes"),
zoomOutDuration.named("Zoom Out Duration"), zoomOutDuration.named("Zoom Out Duration").description("How long zooming back out takes"),
curve.named("Curve")); curve.named("Curve").description("Easing of the zoom"));
} }
// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -77,7 +77,7 @@ public final class PropertyList {
continue; continue;
} }
scroll.draw(new Row(name, width, control.width).position(0, y)); scroll.draw(new Row(name, prop.description, width, control.width).position(0, y));
controls.add(control.position(width - control.width, y + (ROW_HEIGHT - control.height) / 2)); controls.add(control.position(width - control.width, y + (ROW_HEIGHT - control.height) / 2));
y += ROW_HEIGHT; y += ROW_HEIGHT;
} }
@@ -118,17 +118,22 @@ public final class PropertyList {
} }
/** /**
* A row's name and the line under it. The control is a separate element on * A row's name, its description under it, and the line below the row. The
* top, so it gets the clicks and key presses. * control is a separate element on top, so it gets the clicks and key
* presses.
*/ */
private static class Row extends Element { private static class Row extends Element {
private static final float NAME_SCALE = 0.74f; private static final float NAME_SCALE = 0.74f;
private static final float DESCRIPTION_SCALE = 0.62f;
private static final int LINE_GAP = 4;
private final String name; private final String name;
private final String description;
private final int controlWidth; private final int controlWidth;
Row(String name, int width, int controlWidth) { Row(String name, String description, int width, int controlWidth) {
this.name = name; this.name = name;
this.description = description;
this.controlWidth = controlWidth; this.controlWidth = controlWidth;
this.width = width; this.width = width;
this.height = ROW_HEIGHT; this.height = ROW_HEIGHT;
@@ -136,10 +141,24 @@ public final class PropertyList {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
int nameY = (height - (int) (Fonts.getHeight() * NAME_SCALE)) / 2; int textWidth = width - controlWidth - 16;
String text = Fonts.ellipsize(name, Theme.FONT.value, NAME_SCALE, width - controlWidth - 16); int nameHeight = (int) (Fonts.getHeight() * NAME_SCALE);
int descriptionHeight = (int) (Fonts.getHeight() * DESCRIPTION_SCALE);
boolean described = !description.isEmpty();
int blockHeight = described ? nameHeight + LINE_GAP + descriptionHeight : nameHeight;
int nameY = (height - blockHeight) / 2;
renderScope.drawText(NAME_SCALE, Fonts.ellipsize(name, Theme.FONT.value, NAME_SCALE, textWidth),
0, nameY, Theme.FONT.value, Theme.FOREGROUND.value);
if (described) {
int font = Theme.getBodyFont();
renderScope.drawText(DESCRIPTION_SCALE,
Fonts.ellipsize(description, font, DESCRIPTION_SCALE, textWidth),
0, nameY + nameHeight + LINE_GAP, font, Theme.getMutedFg());
}
renderScope.drawText(NAME_SCALE, text, 0, nameY, Theme.FONT.value, Theme.FOREGROUND.value);
renderScope.drawRectangle(0, height - 1, width, 1, Theme.withAlpha(0.06f, Theme.FOREGROUND.value)); renderScope.drawRectangle(0, height - 1, width, 1, Theme.withAlpha(0.06f, Theme.FOREGROUND.value));
} }
} }