Restyle buttons, the Shift menu and the title screen

Button is now primary (accent fill) or secondary (a translucent outlined
surface), and IconButton is a square secondary button with a Lucide icon,
replacing the PNG TextureButtons. Neither is scaled with Element.scale any
more, which multiplied click positions instead of dividing them.

The Shift menu is the HUD editor, the mod menu (its button was labelled
"Settings") and cosmetics. The title screen centers the logo over a
compact stack of the game's menus, with the player, a dock of Saturn's
screens and the version along the bottom. The player chip shows the
player's face from their skin and whether they're connected to Saturn.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-27 19:01:42 +02:00
co-authored by claude
parent 05dbec028a
commit 2f4ea77e6f
7 changed files with 323 additions and 73 deletions
@@ -1,21 +1,40 @@
package org.saturnclient.ui.screens;
import java.util.List;
import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.ref.game.MinecraftClientRef.MinecraftScreen;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.config.Config;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.SaturnScreen;
import org.saturnclient.ui.anim.Fade;
import org.saturnclient.ui.anim.SlideFade;
import org.saturnclient.ui.anim.SlideY;
import org.saturnclient.ui.components.AccountChip;
import org.saturnclient.ui.components.Dock;
import org.saturnclient.ui.elements.AnimationStagger;
import org.saturnclient.ui.elements.Button;
import org.saturnclient.ui.elements.ImageTexture;
import org.saturnclient.ui.elements.TextureButton;
import org.saturnclient.ui.elements.Label;
import org.saturnclient.ui.resources.Fonts;
import org.saturnclient.ui.resources.Textures;
/**
* Saturn's title screen: the logo over the game's menus in the middle, and
* along the bottom the player, a dock of Saturn's screens and the version.
*/
public class TitleMenu extends SaturnScreen {
private static final int LOGO = 88;
private static final int WORDMARK_WIDTH = 200;
private static final int WORDMARK_HEIGHT = 20;
private static final int COLUMN_WIDTH = 280;
private static final int BUTTON_HEIGHT = 36;
private static final int GAP = 8;
private static final int EDGE = 28;
private static final float VERSION_SCALE = 0.62f;
public TitleMenu() {
super("Title Menu");
}
@@ -24,53 +43,73 @@ public class TitleMenu extends SaturnScreen {
public void ui() {
blurBackground = false;
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).center(width, height, 0, -40)
.animation(new Fade(AnimationConfig.logo.duration.value)));
draw(new ImageTexture(Config.getLogo()).dimensions(98, 98).center(width, height, 0, -84)
// Logo and menus, centered together a little above the middle
int menusHeight = BUTTON_HEIGHT * 3 + GAP * 2;
int blockHeight = LOGO + 16 + WORDMARK_HEIGHT + 36 + menusHeight;
int top = (height - blockHeight) / 2 - 20;
draw(new ImageTexture(Config.getLogo())
.dimensions(LOGO, LOGO)
.position((width - LOGO) / 2, top)
.animation(new SlideY(AnimationConfig.logo, -20)));
draw(new ImageTexture(Textures.LOGO_TEXT_BIG)
.dimensions(WORDMARK_WIDTH, WORDMARK_HEIGHT)
.position((width - WORDMARK_WIDTH) / 2, top + LOGO + 16)
.animation(new Fade(AnimationConfig.logo.duration.value)));
AnimationStagger mainButtonStagger = new AnimationStagger(AnimationConfig.mainMenu);
int rowHeight = BUTTON_HEIGHT + GAP;
int halfWidth = (COLUMN_WIDTH - GAP) / 2;
AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu);
mainButtonStagger.draw(new Button("SINGLEPLAYER", () -> {
Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld);
}).scale(0.69f).dimensions(420, 52).position(0, 0)
.animation(new SlideFade(AnimationConfig.mainMenu, -10)));
addMenuButton(menus, new Button("Singleplayer", true,
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld))
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0));
addMenuButton(menus, new Button("Multiplayer", false,
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer))
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, rowHeight));
addMenuButton(menus, new Button("Options", false,
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options))
.dimensions(halfWidth, BUTTON_HEIGHT).position(0, rowHeight * 2));
addMenuButton(menus, new Button("Quit", false,
() -> Providers.saturn.getClient().scheduleStop())
.dimensions(halfWidth, BUTTON_HEIGHT).position(COLUMN_WIDTH - halfWidth, rowHeight * 2));
mainButtonStagger.draw(new Button("MULTIPLAYER", () -> {
Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer);
}).scale(0.69f).dimensions(420, 52).position(0, 40)
.animation(new SlideFade(AnimationConfig.mainMenu, -10)));
draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + 16 + WORDMARK_HEIGHT + 36));
mainButtonStagger.draw(new Button("OPTIONS", () -> {
Providers.saturn.getClient().setScreen(MinecraftScreen.Options);
}).scale(0.69f).dimensions(206, 52).position(0, 80)
.animation(new SlideFade(AnimationConfig.mainMenu, -10)));
// Along the bottom: the player, Saturn's screens, the version
draw(new AccountChip()
.position(EDGE, height - EDGE - AccountChip.HEIGHT)
.animation(new Fade(AnimationConfig.mainMenu)));
mainButtonStagger.draw(new Button("QUIT", () -> {
Providers.saturn.getClient().scheduleStop();
}).scale(0.69f).dimensions(206, 52).position(146, 80)
.animation(new SlideFade(AnimationConfig.mainMenu, -10)));
Dock dock = new Dock(List.of(
new Dock.Item(Textures.ICON_MODS, "Mods",
() -> Providers.saturn.getClient().setScreen(new ModMenu())),
new Dock.Item(Textures.ICON_COSMETICS, "Cosmetics",
() -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())),
new Dock.Item(Textures.ICON_HUD_EDITOR, "HUD editor",
() -> Providers.saturn.getClient().setScreen(new HudEditor()))));
draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT)
.animation(new SlideFade(AnimationConfig.mainMenu, 12)));
draw(mainButtonStagger.dimensions(289, 143).center(width, height, 0, 50));
String version = getVersionText();
int versionWidth = (int) (Fonts.getWidth(version, Theme.getBodyFont()) * VERSION_SCALE);
int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE);
draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont, Theme::getMutedFg)
.position(width - EDGE - versionWidth, height - EDGE - (AccountChip.HEIGHT + versionHeight) / 2)
.animation(new Fade(AnimationConfig.mainMenu)));
}
int s = Fonts.getHeight() + 20;
/**
* "Saturn Client 0.1.0 · Minecraft 1.21.11", from the mod version
* "0.1.0+1.21.11".
*/
private static String getVersionText() {
String[] version = Providers.saturn.getModVersion().split("\\+", 2);
String text = "Saturn Client " + version[0];
return version.length > 1 ? text + " · Minecraft " + version[1] : text;
}
AnimationStagger saturnStagger = new AnimationStagger(AnimationConfig.mainMenu.stagger.value);
saturnStagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
Providers.saturn.getClient().setScreen(new HudEditor());
}).dimensions(s, s).position(0, 0).animation(new SlideFade(AnimationConfig.mainMenu, s + 8)));
saturnStagger.draw(new TextureButton(Textures.SETTINGS, () -> {
Providers.saturn.getClient().setScreen(new ModMenu());
}).dimensions(s, s).position(s + 2, 0)
.animation(new SlideFade(AnimationConfig.mainMenu, s + 8)));
saturnStagger.draw(new TextureButton(Textures.CLOAK, () -> {
Providers.saturn.getClient().setScreen(new CosmeticsMenu());
}).dimensions(s, s).position((s + 2) * 2, 0)
.animation(new SlideFade(AnimationConfig.mainMenu, s + 8)));
draw(saturnStagger.dimensions(((s + 2) * 2) + s, s).centerHorizontal(width, height, 0, 8));
private static void addMenuButton(AnimationStagger stagger, Element button) {
stagger.draw(button.animation(new SlideFade(AnimationConfig.mainMenu, -10)));
}
}