Describe every setting under its name

Properties can carry a one-line description, set with
NamedProperty.description for mods, a ConfigManager.property overload for
the client's config, and a helper in ModLayout. PropertyList rows show it
under the setting's name in muted text. Every mod, HUD layout, general,
UI style and animation setting now has one.

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