Add GUI scale and glass, and rework the title screen #8

Merged
selimaj-dev merged 10 commits from glass-blur into master 2026-09-28 20:01:19 +00:00
4 changed files with 59 additions and 33 deletions
Showing only changes of commit a7ad458627 - Show all commits
@@ -26,6 +26,11 @@ public class ElementRenderer {
for (Element element : elements) { for (Element element : elements) {
element.playAnimationFrame(elapsed); element.playAnimationFrame(elapsed);
// Not faded in yet
if (element.opacity <= 0) {
continue;
}
renderScope.getMatrixStack().push(); renderScope.getMatrixStack().push();
renderScope.setOpacity(element.opacity); renderScope.setOpacity(element.opacity);
renderScope.getMatrixStack().translate(element.x, element.y); renderScope.getMatrixStack().translate(element.x, element.y);
@@ -16,12 +16,12 @@ public class Fade extends Animation {
@Override @Override
public void tick(double progress, Element element) { public void tick(double progress, Element element) {
element.opacity = Math.max(0.1f, targetOpacity * (float) progress); element.opacity = targetOpacity * (float) progress;
} }
@Override @Override
public void init(Element element) { public void init(Element element) {
targetOpacity = element.opacity; targetOpacity = element.opacity;
element.opacity = 0.1f; element.opacity = 0;
} }
} }
@@ -13,6 +13,8 @@ import org.saturnclient.ui.RenderScope;
public class AnimationStagger extends Element { public class AnimationStagger extends Element {
protected List<Element> children = new ArrayList<>(); protected List<Element> children = new ArrayList<>();
public int delay; public int delay;
/** When the first child starts, in ms */
public int start = 0;
public AnimationStagger(int delay) { public AnimationStagger(int delay) {
this.delay = delay; this.delay = delay;
@@ -22,13 +24,21 @@ public class AnimationStagger extends Element {
this.delay = config.stagger.value; this.delay = config.stagger.value;
} }
/**
* Holds the children back until {@code start} ms after the screen opens.
*/
public AnimationStagger after(int start) {
this.start = start;
return this;
}
public void draw(Element element) { public void draw(Element element) {
if (!Config.stagger.value) { if (!Config.stagger.value) {
element.animation = null; element.animation = null;
} }
if (element.animation != null) { if (element.animation != null) {
element.animation.delay = delay * children.size(); element.animation.delay = start + delay * children.size();
} }
ElementRenderer.draw(children, element); ElementRenderer.draw(children, element);
@@ -10,6 +10,7 @@ import org.saturnclient.config.property.FloatProperty;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen;
import org.saturnclient.ui.anim.Animation;
import org.saturnclient.ui.anim.Fade; import org.saturnclient.ui.anim.Fade;
import org.saturnclient.ui.anim.SlideFade; import org.saturnclient.ui.anim.SlideFade;
import org.saturnclient.ui.anim.SlideY; import org.saturnclient.ui.anim.SlideY;
@@ -17,14 +18,16 @@ import org.saturnclient.ui.components.AccountChip;
import org.saturnclient.ui.components.Dock; import org.saturnclient.ui.components.Dock;
import org.saturnclient.ui.elements.AnimationStagger; import org.saturnclient.ui.elements.AnimationStagger;
import org.saturnclient.ui.elements.Button; import org.saturnclient.ui.elements.Button;
import org.saturnclient.ui.elements.IconButton;
import org.saturnclient.ui.elements.ImageTexture; import org.saturnclient.ui.elements.ImageTexture;
import org.saturnclient.ui.elements.Label; import org.saturnclient.ui.elements.Label;
import org.saturnclient.ui.resources.Fonts; import org.saturnclient.ui.resources.Fonts;
import org.saturnclient.ui.resources.Textures; import org.saturnclient.ui.resources.Textures;
/** /**
* Saturn's title screen: the logo over the game's menus in the middle, and * Saturn's title screen: the logo over the game's menus in the middle, the
* along the bottom the player, a dock of Saturn's screens and the version. * player and a quit button in the top corners, and along the bottom a dock of
* Saturn's screens and Minecraft's options, and the version.
*/ */
public class TitleMenu extends SaturnScreen { public class TitleMenu extends SaturnScreen {
// The logo and wordmark as the shift menu has them: the wordmark over the logo's foot // The logo and wordmark as the shift menu has them: the wordmark over the logo's foot
@@ -37,8 +40,16 @@ public class TitleMenu extends SaturnScreen {
private static final int COLUMN_WIDTH = 280; private static final int COLUMN_WIDTH = 280;
private static final int BUTTON_HEIGHT = 36; private static final int BUTTON_HEIGHT = 36;
private static final int GAP = 8; private static final int GAP = 8;
private static final int EDGE = 28; /** Space around the account chip, quit button, dock and version, from the screen's edges */
private static final float VERSION_SCALE = 0.62f; private static final int EDGE = 14;
private static final int QUIT_SIZE = 32;
// The intro, in ms: the logo, then the menus, then what's around the edges
private static final int MENUS_DELAY = 250;
private static final int EDGES_DELAY = 450;
private static final int EDGES_STAGGER = 60;
private static final float VERSION_SCALE = 0.5f;
private static final float VERSION_ALPHA = 0.6f;
public TitleMenu() { public TitleMenu() {
super("Title Menu"); super("Title Menu");
@@ -54,7 +65,7 @@ public class TitleMenu extends SaturnScreen {
blurBackground = false; blurBackground = false;
// Logo and menus, centered together a little above the middle // Logo and menus, centered together a little above the middle
int menusHeight = BUTTON_HEIGHT * 3 + GAP * 2; int menusHeight = BUTTON_HEIGHT * 2 + GAP;
int blockHeight = LOGO + MENUS_GAP + menusHeight; int blockHeight = LOGO + MENUS_GAP + menusHeight;
int top = (height - blockHeight) / 2 - 20; int top = (height - blockHeight) / 2 - 20;
@@ -67,57 +78,57 @@ public class TitleMenu extends SaturnScreen {
.position((width - WORDMARK_WIDTH) / 2, top + WORDMARK_Y) .position((width - WORDMARK_WIDTH) / 2, top + WORDMARK_Y)
.animation(new Fade(AnimationConfig.logo.duration.value))); .animation(new Fade(AnimationConfig.logo.duration.value)));
int rowHeight = BUTTON_HEIGHT + GAP; AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu).after(MENUS_DELAY);
int halfWidth = (COLUMN_WIDTH - GAP) / 2;
AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu);
addMenuButton(menus, new Button("Singleplayer", addMenuButton(menus, new Button("Singleplayer",
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld)) () -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld))
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0)); .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0));
addMenuButton(menus, new Button("Multiplayer", addMenuButton(menus, new Button("Multiplayer",
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer)) () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer))
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, rowHeight)); .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, BUTTON_HEIGHT + GAP));
addMenuButton(menus, new Button("Options",
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options))
.dimensions(halfWidth, BUTTON_HEIGHT).position(0, rowHeight * 2));
addMenuButton(menus, new Button("Quit",
() -> Providers.saturn.getClient().scheduleStop())
.dimensions(halfWidth, BUTTON_HEIGHT).position(COLUMN_WIDTH - halfWidth, rowHeight * 2));
draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + MENUS_GAP)); draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + MENUS_GAP));
// Along the bottom: the player, Saturn's screens, the version // Top corners: the player, and quitting the game
draw(new AccountChip() draw(new AccountChip()
.position(EDGE, height - EDGE - AccountChip.HEIGHT) .position(EDGE, EDGE)
.animation(new Fade(AnimationConfig.mainMenu))); .animation(delayed(new SlideFade(AnimationConfig.mainMenu, 8), EDGES_DELAY)));
draw(new IconButton(Textures.ICON_CLOSE, () -> Providers.saturn.getClient().scheduleStop())
.dimensions(QUIT_SIZE, QUIT_SIZE)
.position(width - EDGE - QUIT_SIZE, EDGE + (AccountChip.HEIGHT - QUIT_SIZE) / 2)
.animation(delayed(new SlideFade(AnimationConfig.mainMenu, 8), EDGES_DELAY)));
// Along the bottom: the dock of Saturn's screens and Minecraft's options, and the version
Dock dock = new Dock(List.of( Dock dock = new Dock(List.of(
new Dock.Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())), new Dock.Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())),
new Dock.Item(Textures.ICON_COSMETICS, new Dock.Item(Textures.ICON_COSMETICS,
() -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())), () -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())),
new Dock.Item(Textures.ICON_HUD_EDITOR, new Dock.Item(Textures.LOGO, () -> Providers.saturn.getClient().setScreen(new ClientSettings())),
() -> Providers.saturn.getClient().setScreen(new HudEditor())),
new Dock.Item(Textures.ICON_SETTINGS, new Dock.Item(Textures.ICON_SETTINGS,
() -> Providers.saturn.getClient().setScreen(new ClientSettings())))); () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options))));
draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT) draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT)
.animation(new SlideFade(AnimationConfig.mainMenu, 12))); .animation(delayed(new SlideFade(AnimationConfig.mainMenu, -12), EDGES_DELAY + EDGES_STAGGER)));
String version = getVersionText(); String version = getVersionText();
int versionWidth = (int) (Fonts.getWidth(version, Theme.getBodyFont()) * VERSION_SCALE);
int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE); int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE);
draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont, Theme::getMutedFg) draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont,
.position(width - EDGE - versionWidth, height - EDGE - (AccountChip.HEIGHT + versionHeight) / 2) () -> Theme.withAlpha(VERSION_ALPHA, Theme.getMutedFg()))
.animation(new Fade(AnimationConfig.mainMenu))); .position(EDGE, height - EDGE - versionHeight)
.animation(delayed(new Fade(AnimationConfig.mainMenu), EDGES_DELAY + EDGES_STAGGER * 2)));
}
private static Animation delayed(Animation animation, int delay) {
animation.delay = delay;
return animation;
} }
/** /**
* "Saturn Client 0.1.0 · Minecraft 1.21.11", from the mod version * "Saturn 0.1.0 · 1.21.11", from the mod version "0.1.0+1.21.11".
* "0.1.0+1.21.11".
*/ */
private static String getVersionText() { private static String getVersionText() {
String[] version = Providers.saturn.getModVersion().split("\\+", 2); String[] version = Providers.saturn.getModVersion().split("\\+", 2);
String text = "Saturn Client " + version[0]; String text = "Saturn " + version[0];
return version.length > 1 ? text + " · Minecraft " + version[1] : text; return version.length > 1 ? text + " · " + version[1] : text;
} }
private static void addMenuButton(AnimationStagger stagger, Element button) { private static void addMenuButton(AnimationStagger stagger, Element button) {