Compare commits
11
Commits
ef0ead6bf8
...
7d0bfd26c9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d0bfd26c9 | ||
|
|
7cc37b7c74 | ||
|
|
524fc36bf3 | ||
|
|
7bc308155e | ||
|
|
4d9c629eaf | ||
|
|
5c0912f0cf | ||
|
|
3436054112 | ||
|
|
adedd688a7 | ||
|
|
a7ad458627 | ||
|
|
320b2fd9a5 | ||
|
|
a7e3e537b1 |
@@ -6,6 +6,7 @@ import org.saturnclient.common.provider.GLFWProvider;
|
||||
import org.saturnclient.common.provider.Providers;
|
||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||
import org.saturnclient.config.property.BoolProperty;
|
||||
import org.saturnclient.config.property.FloatProperty;
|
||||
import org.saturnclient.config.property.IntProperty;
|
||||
import org.saturnclient.config.property.KeybindingProperty;
|
||||
import org.saturnclient.config.property.Property;
|
||||
@@ -18,9 +19,30 @@ public class Config {
|
||||
public static BoolProperty saturnTitleScreen = Property.bool(true);
|
||||
public static BoolProperty cloakPhysics = Property.bool(true);
|
||||
public static BoolProperty stagger = Property.bool(true);
|
||||
/** The world or panorama behind Saturn's screens, faded in as they open */
|
||||
public static IntProperty backgroundBlur = Property.integer(5).range(0, 10);
|
||||
/** Glass behind translucent menus (panels), and widgets (buttons, the dock) */
|
||||
public static IntProperty menuBlur = Property.integer(8).range(0, 20);
|
||||
public static IntProperty widgetBlur = Property.integer(6).range(0, 20);
|
||||
// How big Saturn's screens are, relative to Minecraft's GUI scale
|
||||
public static FloatProperty titleScale = guiScale();
|
||||
public static FloatProperty menuScale = guiScale();
|
||||
public static FloatProperty hudMenuScale = guiScale();
|
||||
public static KeybindingProperty openEmoteWheel = Property.keybinding(GLFWProvider.GLFW_KEY_B);
|
||||
|
||||
private static FloatProperty guiScale() {
|
||||
return Property.floatProp(1f).range(0.5f, 2f, 0.05f).unit("×");
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any glass can be drawn: a glass blur is on, and a background it
|
||||
* goes behind isn't solid.
|
||||
*/
|
||||
public static boolean usesGlass() {
|
||||
boolean translucent = Theme.getPanelBg() >>> 24 < 0xFF || Theme.PRIMARY.value >>> 24 < 0xFF;
|
||||
return translucent && (menuBlur.value > 0 || widgetBlur.value > 0);
|
||||
}
|
||||
|
||||
public static IdentifierRef getLogo() {
|
||||
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
|
||||
}
|
||||
@@ -34,7 +56,12 @@ public class Config {
|
||||
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");
|
||||
config.property("Background blur", backgroundBlur, "How much the game behind Saturn's screens is blurred");
|
||||
config.property("World blur", backgroundBlur, "How much the world or panorama behind Saturn's screens is blurred");
|
||||
config.property("Menu blur", menuBlur, "How much the game is blurred through translucent menus");
|
||||
config.property("Widget blur", widgetBlur, "How much the game is blurred through translucent buttons and the dock");
|
||||
config.property("Title screen scale", titleScale, "How big the title screen is, relative to Minecraft's GUI scale");
|
||||
config.property("Menu scale", menuScale, "How big the mods, settings and cosmetics menus are, relative to Minecraft's GUI scale");
|
||||
config.property("HUD menu scale", hudMenuScale, "How big the emote wheel and shift menu are, relative to Minecraft's GUI scale");
|
||||
|
||||
// Initialize a sub namespace for theme
|
||||
Theme.init(config);
|
||||
|
||||
@@ -10,14 +10,14 @@ import org.saturnclient.config.property.SelectProperty;
|
||||
public class Theme {
|
||||
public static ConfigManager config;
|
||||
|
||||
// Main UI background (slightly warm dark to match Saturn tone)
|
||||
public static final ColorProperty BACKGROUND = Property.color(0xFF12100D);
|
||||
// Main UI background (slightly warm dark to match Saturn tone), translucent over the world
|
||||
public static final ColorProperty BACKGROUND = Property.color(0xD112100D);
|
||||
|
||||
// Default text color
|
||||
public static final ColorProperty FOREGROUND = Property.color(0xFFF2E6D2);
|
||||
|
||||
// Widget default background (normal state)
|
||||
public static final ColorProperty PRIMARY = Property.color(0xFF1C1915);
|
||||
// Widget default background (normal state), translucent over the world
|
||||
public static final ColorProperty PRIMARY = Property.color(0xB81C1915);
|
||||
|
||||
// Widget text color
|
||||
public static final ColorProperty PRIMARY_FG = Property.color(0xFFEAD9B8);
|
||||
@@ -62,10 +62,10 @@ public class Theme {
|
||||
}
|
||||
|
||||
public static final List<Preset> PRESETS = List.of(
|
||||
new Preset("Saturn", 0xFF12100D, 0xFFF2E6D2, 0xFF1C1915, 0xFFEAD9B8, 0xFFE2BF7D, 0xFF1A140A, 0xFFE2BF7D),
|
||||
new Preset("Midnight", 0xFF0E1117, 0xFFE4EAF5, 0xFF171C26, 0xFFC9D4E8, 0xFF8FB3FF, 0xFF0A1224, 0xFF8FB3FF),
|
||||
new Preset("Amethyst", 0xFF120F17, 0xFFEDE4F7, 0xFF1C1724, 0xFFD8C9EB, 0xFFC79BFF, 0xFF1A0F2A, 0xFFC79BFF),
|
||||
new Preset("Emerald", 0xFF0D1210, 0xFFE2F2EA, 0xFF16201B, 0xFFC4E0D2, 0xFF7FD1A8, 0xFF0A1F15, 0xFF7FD1A8));
|
||||
new Preset("Saturn", 0xD112100D, 0xFFF2E6D2, 0xB81C1915, 0xFFEAD9B8, 0xFFE2BF7D, 0xFF1A140A, 0xFFE2BF7D),
|
||||
new Preset("Midnight", 0xD10E1117, 0xFFE4EAF5, 0xB8171C26, 0xFFC9D4E8, 0xFF8FB3FF, 0xFF0A1224, 0xFF8FB3FF),
|
||||
new Preset("Amethyst", 0xD1120F17, 0xFFEDE4F7, 0xB81C1724, 0xFFD8C9EB, 0xFFC79BFF, 0xFF1A0F2A, 0xFFC79BFF),
|
||||
new Preset("Emerald", 0xD10D1210, 0xFFE2F2EA, 0xB816201B, 0xFFC4E0D2, 0xFF7FD1A8, 0xFF0A1F15, 0xFF7FD1A8));
|
||||
|
||||
public static void init(ConfigManager parent) {
|
||||
config = new ConfigManager(parent, "UI Style");
|
||||
@@ -98,10 +98,19 @@ public class Theme {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translucent background for panels drawn over the world
|
||||
* Background for panels drawn over the world, as translucent as the
|
||||
* background color is
|
||||
*/
|
||||
public static int getPanelBg() {
|
||||
return withAlpha(0.82f, BACKGROUND.value);
|
||||
return BACKGROUND.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Background for popups, which sit over other panels: the background color,
|
||||
* kept nearly solid so what's under it doesn't show through
|
||||
*/
|
||||
public static int getPopupBg() {
|
||||
return withAlpha(0.97f, BACKGROUND.value | 0xFF000000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,11 @@ public class ElementRenderer {
|
||||
for (Element element : elements) {
|
||||
element.playAnimationFrame(elapsed);
|
||||
|
||||
// Not faded in yet
|
||||
if (element.opacity <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
renderScope.getMatrixStack().push();
|
||||
renderScope.setOpacity(element.opacity);
|
||||
renderScope.getMatrixStack().translate(element.x, element.y);
|
||||
|
||||
@@ -34,6 +34,14 @@ public interface RenderScope {
|
||||
if (color == 0)
|
||||
return;
|
||||
|
||||
drawTexture(getRoundedRectangle(width, height, radius), x, y, 0, 0, width, height, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* The white rounded rectangle that {@link #drawRoundedRectangle} tints, as a
|
||||
* cached texture.
|
||||
*/
|
||||
default IdentifierRef getRoundedRectangle(int width, int height, int radius) {
|
||||
radius = Math.min(radius, width / 2);
|
||||
radius = Math.min(radius, height / 2);
|
||||
|
||||
@@ -42,7 +50,38 @@ public interface RenderScope {
|
||||
width, height,
|
||||
radius, radius);
|
||||
|
||||
drawSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, x, y, width, height, color);
|
||||
return getSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frosts a rounded rectangle for a translucent background to be drawn over:
|
||||
* the game behind the screen, blurred by {@code blur}, cut to exactly the
|
||||
* shape {@link #drawRoundedRectangle} draws. Render scopes without glass
|
||||
* draw nothing.
|
||||
*/
|
||||
default void drawGlass(int x, int y, int width, int height, int radius, int blur) {
|
||||
drawGlass(getRoundedRectangle(width, height, radius), x, y, width, height, blur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the game behind the screen, blurred by {@code blur}, where
|
||||
* {@code mask}, stretched over the area, is opaque.
|
||||
*/
|
||||
default void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link #drawRoundedRectangle} with a border, frosted with glass blurred
|
||||
* by {@code blur} when the background isn't solid. A blur of 0 leaves it
|
||||
* plain, showing the screen's own background blur.
|
||||
*/
|
||||
default void drawGlassRectangle(int x, int y, int width, int height, int radius, int blur, int thickness,
|
||||
int background, int border) {
|
||||
if (blur > 0 && background >>> 24 < 0xFF) {
|
||||
drawGlass(x, y, width, height, radius, blur);
|
||||
}
|
||||
|
||||
drawRoundedRectangle(x, y, width, height, radius, thickness, background, border);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,15 +128,22 @@ public interface RenderScope {
|
||||
* {@code color}.
|
||||
*/
|
||||
private void drawSvgShape(String shape, String cacheKey, int x, int y, int width, int height, int color) {
|
||||
drawTexture(getSvgShape(shape, cacheKey, width, height), x, y, 0, 0, width, height, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code shape} (SVG elements in a {@code width} x {@code height} canvas,
|
||||
* drawn in white) rendered to a cached texture.
|
||||
*/
|
||||
private IdentifierRef getSvgShape(String shape, String cacheKey, int width, int height) {
|
||||
String svg = String.format(
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>%s</svg>",
|
||||
width, height, shape);
|
||||
|
||||
InputStream svgStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
drawTexture(SvgTexture.getSvg(svgStream,
|
||||
IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey),
|
||||
width * 4, height * 4), x, y, 0, 0, width, height, color);
|
||||
return SvgTexture.getSvg(svgStream, IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey),
|
||||
width * 4, height * 4);
|
||||
}
|
||||
|
||||
void drawBorder(int x, int y, int width, int height, int color);
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.property.FloatProperty;
|
||||
|
||||
public abstract class SaturnScreen {
|
||||
/** How long the background blur takes to fade in, in ms */
|
||||
@@ -14,8 +15,10 @@ public abstract class SaturnScreen {
|
||||
public static interface ScreenProvider {
|
||||
public void close();
|
||||
|
||||
/** The screen's width in GUI pixels */
|
||||
public int getWidth();
|
||||
|
||||
/** The screen's height in GUI pixels */
|
||||
public int getHeight();
|
||||
}
|
||||
|
||||
@@ -23,6 +26,10 @@ public abstract class SaturnScreen {
|
||||
public int width;
|
||||
public int height;
|
||||
public String title;
|
||||
/** GUI pixels per Saturn unit, from {@link #getScale()} when the screen was built */
|
||||
public float scale = 0.5f;
|
||||
/** Whether a mouse button is held, so the screen isn't rebuilt under a drag */
|
||||
private boolean mouseDown = false;
|
||||
|
||||
// Saturn Screen
|
||||
public List<Element> elements = new ArrayList<>();
|
||||
@@ -51,10 +58,39 @@ public abstract class SaturnScreen {
|
||||
return Math.round(Config.backgroundBlur.value * Math.min(elapsed / (float) BLUR_FADE_IN, 1f));
|
||||
}
|
||||
|
||||
/**
|
||||
* The GUI scale setting this screen follows; the menu scale unless the
|
||||
* screen is another kind.
|
||||
*/
|
||||
protected FloatProperty getScaleSetting() {
|
||||
return Config.menuScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* GUI pixels per Saturn unit. At 1× a Saturn unit is half a GUI pixel.
|
||||
* Enlarging stops where the screen would be smaller than 320x240 GUI
|
||||
* pixels, the least Minecraft's own GUI scale leaves.
|
||||
*/
|
||||
public float getScale() {
|
||||
float fit = Math.min(provider.getWidth() / 320f, provider.getHeight() / 240f);
|
||||
return 0.5f * Math.min(getScaleSetting().value, Math.max(1f, fit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the screen if its scale setting changed, once no drag is going
|
||||
* on (the slider being dragged would be replaced).
|
||||
*/
|
||||
public void updateScale() {
|
||||
if (!mouseDown && getScale() != scale) {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
public void init() {
|
||||
elements.clear();
|
||||
width = provider.getWidth();
|
||||
height = provider.getHeight();
|
||||
scale = getScale();
|
||||
width = (int) (provider.getWidth() / scale);
|
||||
height = (int) (provider.getHeight() / scale);
|
||||
Popup.reset(width, height);
|
||||
ui(); // abstraction to render the saturn ui and also render extra stuff here
|
||||
}
|
||||
@@ -72,7 +108,7 @@ public abstract class SaturnScreen {
|
||||
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
|
||||
renderScope.getMatrixStack().push();
|
||||
|
||||
renderScope.getMatrixStack().scale(0.5f, 0.5f);
|
||||
renderScope.getMatrixStack().scale(scale, scale);
|
||||
|
||||
ElementRenderer.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY);
|
||||
Popup.render(renderScope, elapsed, mouseX, mouseY);
|
||||
@@ -85,8 +121,9 @@ public abstract class SaturnScreen {
|
||||
// --------------
|
||||
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
mouseDown = true;
|
||||
mouseX /= scale;
|
||||
mouseY /= scale;
|
||||
|
||||
if (!Popup.mouseClicked(mouseX, mouseY, button)) {
|
||||
ElementRenderer.mouseClicked(elements, mouseX, mouseY, button);
|
||||
@@ -96,8 +133,8 @@ public abstract class SaturnScreen {
|
||||
}
|
||||
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
mouseX /= scale;
|
||||
mouseY /= scale;
|
||||
|
||||
if (!Popup.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) {
|
||||
ElementRenderer.mouseDragged(elements, mouseX, mouseY, button, deltaX, deltaY);
|
||||
@@ -107,8 +144,9 @@ public abstract class SaturnScreen {
|
||||
}
|
||||
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
mouseDown = false;
|
||||
mouseX /= scale;
|
||||
mouseY /= scale;
|
||||
|
||||
// Everything hears the release, so no element is left thinking it's being dragged.
|
||||
Popup.mouseReleased(mouseX, mouseY, button);
|
||||
@@ -122,8 +160,8 @@ public abstract class SaturnScreen {
|
||||
double mouseY,
|
||||
double horizontalAmount,
|
||||
double verticalAmount) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
mouseX /= scale;
|
||||
mouseY /= scale;
|
||||
|
||||
if (!Popup.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)) {
|
||||
ElementRenderer.mouseScrolled(elements, mouseX, mouseY, horizontalAmount, verticalAmount);
|
||||
|
||||
@@ -16,12 +16,12 @@ public class Fade extends Animation {
|
||||
|
||||
@Override
|
||||
public void tick(double progress, Element element) {
|
||||
element.opacity = Math.max(0.1f, targetOpacity * (float) progress);
|
||||
element.opacity = targetOpacity * (float) progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Element element) {
|
||||
targetOpacity = element.opacity;
|
||||
element.opacity = 0.1f;
|
||||
element.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.saturnclient.ui.components;
|
||||
import org.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.common.provider.Providers;
|
||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
@@ -15,12 +16,13 @@ import org.saturnclient.ui.resources.Fonts;
|
||||
* while connected to Saturn.
|
||||
*/
|
||||
public class AccountChip extends Element {
|
||||
public static final int HEIGHT = 40;
|
||||
public static final int HEIGHT = 32;
|
||||
|
||||
private static final int HEAD = 28;
|
||||
private static final int PADDING = 6;
|
||||
private static final int DOT = 7;
|
||||
private static final float NAME_SCALE = 0.7f;
|
||||
private static final int HEAD = 18;
|
||||
private static final int PADDING = 7;
|
||||
private static final int GAP = 8;
|
||||
private static final int DOT = 6;
|
||||
private static final float NAME_SCALE = 0.55f;
|
||||
private static final int CONNECTED = 0xFF7FD1A8;
|
||||
|
||||
private final String name;
|
||||
@@ -29,13 +31,14 @@ public class AccountChip extends Element {
|
||||
String username = Providers.saturn.getClient().getUsername();
|
||||
this.name = username != null ? username : "Player";
|
||||
this.height = HEIGHT;
|
||||
this.width = PADDING + HEAD + 10 + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + 10 + DOT + 12;
|
||||
this.width = PADDING + HEAD + GAP + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + GAP + DOT
|
||||
+ GAP + 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, Theme.getPanelBg(),
|
||||
Theme.getBorder());
|
||||
renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Config.widgetBlur.value, 1,
|
||||
Theme.getPanelBg(), Theme.getBorder());
|
||||
|
||||
// The face, then the hat layer over it, from the 64x64 skin
|
||||
IdentifierRef skin = Providers.saturn.getPlayerSkin();
|
||||
@@ -43,11 +46,11 @@ public class AccountChip extends Element {
|
||||
renderScope.drawTexture(skin, PADDING, headY, 8, 8, HEAD, HEAD, 8, 8, 64, 64);
|
||||
renderScope.drawTexture(skin, PADDING, headY, 40, 8, HEAD, HEAD, 8, 8, 64, 64);
|
||||
|
||||
int nameX = PADDING + HEAD + 10;
|
||||
int nameX = PADDING + HEAD + GAP;
|
||||
renderScope.drawText(NAME_SCALE, name, nameX, Field.textY(height, NAME_SCALE), Theme.FONT.value,
|
||||
Theme.FOREGROUND.value);
|
||||
|
||||
int dotX = nameX + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + 10;
|
||||
int dotX = nameX + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + GAP;
|
||||
renderScope.drawRoundedRectangle(dotX, (height - DOT) / 2, DOT, DOT, DOT / 2,
|
||||
ServiceClient.isConnected() ? CONNECTED : Theme.withAlpha(0.3f, Theme.FOREGROUND.value));
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class CosmeticTile extends Element {
|
||||
int border = isEquipped ? Theme.ACCENT.value
|
||||
: Theme.withAlpha(hovered ? 0.22f : 0.06f, Theme.FOREGROUND.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, isEquipped ? 2 : 1,
|
||||
Theme.withAlpha(0.72f, Theme.PRIMARY.value), border);
|
||||
Theme.PRIMARY.value, border);
|
||||
|
||||
// Preview, fitted into its area
|
||||
int areaWidth = width - INSET * 2;
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.saturnclient.ui.components;
|
||||
import java.util.List;
|
||||
|
||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
@@ -15,12 +16,12 @@ public class Dock extends Element {
|
||||
public record Item(IdentifierRef icon, Runnable onClick) {
|
||||
}
|
||||
|
||||
public static final int HEIGHT = 52;
|
||||
public static final int HEIGHT = 40;
|
||||
|
||||
private static final int PADDING = 6;
|
||||
private static final int PADDING = 5;
|
||||
private static final int ITEM = HEIGHT - PADDING * 2;
|
||||
private static final int ICON = 18;
|
||||
private static final int GAP = 4;
|
||||
private static final int ICON = 15;
|
||||
private static final int GAP = 3;
|
||||
|
||||
private final List<Item> items;
|
||||
|
||||
@@ -32,7 +33,8 @@ public class Dock extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value + 4, 1, Theme.getPanelBg(),
|
||||
renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value + 4, Config.widgetBlur.value, 1,
|
||||
Theme.getPanelBg(),
|
||||
Theme.withAlpha(0.1f, Theme.FOREGROUND.value));
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
|
||||
@@ -41,7 +41,7 @@ public class EmoteTile extends Element {
|
||||
boolean hovered = ctx.isHovering();
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, hovered ? 2 : 1,
|
||||
Theme.withAlpha(0.72f, Theme.PRIMARY.value),
|
||||
Theme.PRIMARY.value,
|
||||
hovered ? Theme.ACCENT.value : Theme.withAlpha(0.06f, Theme.FOREGROUND.value));
|
||||
|
||||
int imageSize = height - INSET * 2 - NAME_AREA;
|
||||
|
||||
@@ -46,7 +46,7 @@ public class SaturnModule extends Element {
|
||||
|
||||
// Card
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
|
||||
Theme.withAlpha(0.72f, Theme.PRIMARY.value), border);
|
||||
Theme.PRIMARY.value, border);
|
||||
|
||||
if (enabled) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
|
||||
|
||||
@@ -38,7 +38,7 @@ public class ThemePresetCard extends Element {
|
||||
: Theme.withAlpha(ctx.isHovering() ? 0.22f : 0.06f, Theme.FOREGROUND.value);
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, active ? 2 : 1,
|
||||
Theme.withAlpha(0.72f, Theme.PRIMARY.value), border);
|
||||
Theme.PRIMARY.value, border);
|
||||
|
||||
// A panel in the preset's colors: a line of text over an accent and a widget
|
||||
int previewWidth = width - PADDING * 2;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.saturnclient.ui.components.inputs;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import org.saturnclient.common.provider.Providers;
|
||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.config.property.ColorProperty;
|
||||
import org.saturnclient.ui.Element;
|
||||
@@ -27,6 +30,18 @@ public class ColorPicker extends Element {
|
||||
private static final int SQUARE_Y = OPACITY_Y + SLIDER_HEIGHT + GAP;
|
||||
private static final int HUE_Y = SQUARE_Y + SIZE + GAP;
|
||||
|
||||
// Gradient strips, made once and stretched over each part, so a frame is a
|
||||
// few draws instead of one per pixel. Tinting the white ones colors them.
|
||||
private static final int STRIP = 256;
|
||||
/** White fading out to the right: saturation, over the hue */
|
||||
private static final IdentifierRef SATURATION = IdentifierRef.ofSaturn("textures/gui/color_picker/saturation");
|
||||
/** Black fading in downwards: brightness, over the saturation */
|
||||
private static final IdentifierRef BRIGHTNESS = IdentifierRef.ofSaturn("textures/gui/color_picker/brightness");
|
||||
/** White fading in to the right, tinted with the color */
|
||||
private static final IdentifierRef OPACITY = IdentifierRef.ofSaturn("textures/gui/color_picker/opacity");
|
||||
private static final IdentifierRef HUE = IdentifierRef.ofSaturn("textures/gui/color_picker/hue");
|
||||
private static boolean stripsRegistered = false;
|
||||
|
||||
private final ColorProperty prop;
|
||||
|
||||
private float hue;
|
||||
@@ -46,8 +61,10 @@ public class ColorPicker extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
registerStrips();
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
|
||||
Theme.withAlpha(0.97f, Theme.BACKGROUND.value), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
Theme.getPopupBg(), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
|
||||
renderOpacity(renderScope);
|
||||
renderSquare(renderScope);
|
||||
@@ -55,22 +72,16 @@ public class ColorPicker extends Element {
|
||||
}
|
||||
|
||||
private void renderOpacity(RenderScope renderScope) {
|
||||
int rgb = prop.value & 0xFFFFFF;
|
||||
for (int x = 0; x < SIZE; x++) {
|
||||
int alpha = (int) (x / (float) SIZE * 255) & 0xFF;
|
||||
renderScope.fill(PADDING + x, OPACITY_Y, PADDING + x + 1, OPACITY_Y + SLIDER_HEIGHT, (alpha << 24) | rgb);
|
||||
}
|
||||
renderScope.drawTexture(OPACITY, PADDING, OPACITY_Y, 0, 0, SIZE, SLIDER_HEIGHT,
|
||||
0xFF000000 | prop.value);
|
||||
|
||||
drawMarker(renderScope, PADDING + (int) (opacity * SIZE), OPACITY_Y);
|
||||
}
|
||||
|
||||
private void renderSquare(RenderScope renderScope) {
|
||||
for (int x = 0; x < SIZE; x++) {
|
||||
for (int y = 0; y < SIZE; y++) {
|
||||
int color = Color.HSBtoRGB(hue, x / (float) SIZE, 1f - y / (float) SIZE);
|
||||
renderScope.fill(PADDING + x, SQUARE_Y + y, PADDING + x + 1, SQUARE_Y + y + 1, 0xFF000000 | color);
|
||||
}
|
||||
}
|
||||
renderScope.fill(PADDING, SQUARE_Y, PADDING + SIZE, SQUARE_Y + SIZE, 0xFF000000 | Color.HSBtoRGB(hue, 1f, 1f));
|
||||
renderScope.drawTexture(SATURATION, PADDING, SQUARE_Y, 0, 0, SIZE, SIZE, 0xFFFFFFFF);
|
||||
renderScope.drawTexture(BRIGHTNESS, PADDING, SQUARE_Y, 0, 0, SIZE, SIZE, 0xFFFFFFFF);
|
||||
|
||||
int selX = PADDING + (int) (saturation * SIZE) - 4;
|
||||
int selY = SQUARE_Y + (int) ((1f - brightness) * SIZE) - 4;
|
||||
@@ -79,14 +90,36 @@ public class ColorPicker extends Element {
|
||||
}
|
||||
|
||||
private void renderHue(RenderScope renderScope) {
|
||||
for (int x = 0; x < SIZE; x++) {
|
||||
int color = Color.HSBtoRGB(x / (float) SIZE, 1f, 1f);
|
||||
renderScope.fill(PADDING + x, HUE_Y, PADDING + x + 1, HUE_Y + SLIDER_HEIGHT, 0xFF000000 | color);
|
||||
}
|
||||
renderScope.drawTexture(HUE, PADDING, HUE_Y, 0, 0, SIZE, SLIDER_HEIGHT, 0xFFFFFFFF);
|
||||
|
||||
drawMarker(renderScope, PADDING + (int) (hue * SIZE), HUE_Y);
|
||||
}
|
||||
|
||||
private static void registerStrips() {
|
||||
if (stripsRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedImage saturation = new BufferedImage(STRIP, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
BufferedImage brightness = new BufferedImage(1, STRIP, BufferedImage.TYPE_INT_ARGB);
|
||||
BufferedImage opacity = new BufferedImage(STRIP, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
BufferedImage hue = new BufferedImage(STRIP, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
for (int i = 0; i < STRIP; i++) {
|
||||
int alpha = i * 255 / (STRIP - 1);
|
||||
saturation.setRGB(i, 0, ((255 - alpha) << 24) | 0xFFFFFF);
|
||||
brightness.setRGB(0, i, alpha << 24);
|
||||
opacity.setRGB(i, 0, (alpha << 24) | 0xFFFFFF);
|
||||
hue.setRGB(i, 0, 0xFF000000 | Color.HSBtoRGB(i / (float) STRIP, 1f, 1f));
|
||||
}
|
||||
|
||||
Providers.saturn.registerBufferedImageTexture(SATURATION, saturation);
|
||||
Providers.saturn.registerBufferedImageTexture(BRIGHTNESS, brightness);
|
||||
Providers.saturn.registerBufferedImageTexture(OPACITY, opacity);
|
||||
Providers.saturn.registerBufferedImageTexture(HUE, hue);
|
||||
stripsRegistered = true;
|
||||
}
|
||||
|
||||
private static void drawMarker(RenderScope renderScope, int x, int sliderY) {
|
||||
renderScope.fill(x - 2, sliderY - 2, x + 2, sliderY + SLIDER_HEIGHT + 2, 0xFFFFFFFF);
|
||||
renderScope.fill(x - 1, sliderY - 1, x + 1, sliderY + SLIDER_HEIGHT + 1, 0xFF000000);
|
||||
|
||||
@@ -38,7 +38,7 @@ public class CurvePreview extends Element {
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
|
||||
Theme.withAlpha(0.97f, Theme.BACKGROUND.value), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
Theme.getPopupBg(), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
|
||||
renderScope.drawText(TITLE_SCALE, "PREVIEW", PADDING, PADDING - 2, Theme.FONT.value, Theme.getMutedFg());
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class OptionList extends Element {
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
|
||||
Theme.withAlpha(0.97f, Theme.BACKGROUND.value), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
Theme.getPopupBg(), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
|
||||
|
||||
int visible = Math.min(options.length, MAX_VISIBLE);
|
||||
hovered = getIndexAt(ctx.mouseX, ctx.mouseY);
|
||||
|
||||
@@ -13,6 +13,8 @@ import org.saturnclient.ui.RenderScope;
|
||||
public class AnimationStagger extends Element {
|
||||
protected List<Element> children = new ArrayList<>();
|
||||
public int delay;
|
||||
/** When the first child starts, in ms */
|
||||
public int start = 0;
|
||||
|
||||
public AnimationStagger(int delay) {
|
||||
this.delay = delay;
|
||||
@@ -22,13 +24,21 @@ public class AnimationStagger extends Element {
|
||||
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) {
|
||||
if (!Config.stagger.value) {
|
||||
element.animation = null;
|
||||
}
|
||||
|
||||
if (element.animation != null) {
|
||||
element.animation.delay = delay * children.size();
|
||||
element.animation.delay = start + delay * children.size();
|
||||
}
|
||||
|
||||
ElementRenderer.draw(children, element);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.saturnclient.ui.elements;
|
||||
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
@@ -38,8 +39,8 @@ public class Button extends Element {
|
||||
* The button's surface, shared with {@link IconButton}.
|
||||
*/
|
||||
static void drawSurface(RenderScope renderScope, int width, int height, boolean hovered) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
|
||||
hovered ? Theme.withAlpha(0.9f, Theme.PRIMARY.value) : Theme.getPanelBg(),
|
||||
renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Config.widgetBlur.value, 1,
|
||||
hovered ? Theme.PRIMARY.value : Theme.getPanelBg(),
|
||||
Theme.withAlpha(hovered ? 0.25f : 0.1f, Theme.FOREGROUND.value));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package org.saturnclient.ui.elements;
|
||||
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.ElementContext;
|
||||
import org.saturnclient.ui.RenderScope;
|
||||
|
||||
/**
|
||||
* The translucent, outlined surface that screens lay their content on.
|
||||
* The translucent, outlined surface that screens lay their content on,
|
||||
* frosted over the game where glass is supported.
|
||||
*/
|
||||
public class Panel extends Element {
|
||||
|
||||
@@ -16,7 +18,7 @@ public class Panel extends Element {
|
||||
}
|
||||
|
||||
public static void drawBackground(RenderScope renderScope, int width, int height) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, 1,
|
||||
renderScope.drawGlassRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Config.menuBlur.value, 1,
|
||||
Theme.getPanelBg(), Theme.getBorder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.saturnclient.common.provider.Providers;
|
||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||
import org.saturnclient.config.AnimationConfig;
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.property.FloatProperty;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.cosmetics.Emotes;
|
||||
import org.saturnclient.ui.Element;
|
||||
@@ -43,6 +44,11 @@ public class EmoteWheel extends SaturnScreen {
|
||||
super("Emote Wheel");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FloatProperty getScaleSetting() {
|
||||
return Config.hudMenuScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
List<String> emotes = Emotes.availableEmotes;
|
||||
@@ -125,8 +131,8 @@ public class EmoteWheel extends SaturnScreen {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
|
||||
Theme.getPanelBg(), Theme.getBorder());
|
||||
renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Config.widgetBlur.value,
|
||||
1, Theme.getPanelBg(), Theme.getBorder());
|
||||
|
||||
float titleScale = 0.54f;
|
||||
drawCentered(renderScope, "EMOTES", titleScale, pages > 1 ? 24 : (height - 10) / 2, Theme.getMutedFg());
|
||||
|
||||
@@ -18,8 +18,8 @@ import org.saturnclient.ui.resources.Fonts;
|
||||
* Moves and resizes the enabled HUD mods, drawn as they are in game. Drag one
|
||||
* to move it, or drag its corner handle to resize it.
|
||||
*
|
||||
* Unlike other Saturn screens this works in GUI pixels, like the HUD itself:
|
||||
* it draws without the half scale and takes the mouse unscaled.
|
||||
* Unlike other Saturn screens this works in GUI pixels, like the HUD itself,
|
||||
* whatever Saturn's GUI scale.
|
||||
*/
|
||||
public class HudEditor extends SaturnScreen {
|
||||
private static final String HINT = "Drag to move · Drag the corner to resize · Esc when done";
|
||||
@@ -45,6 +45,11 @@ public class HudEditor extends SaturnScreen {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScale() {
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
// Placing HUDs needs a clear view of the game.
|
||||
@@ -102,10 +107,7 @@ public class HudEditor extends SaturnScreen {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
|
||||
// The screen hands over the mouse in Saturn units, twice GUI pixels.
|
||||
int guiMouseX = mouseX / 2;
|
||||
int guiMouseY = mouseY / 2;
|
||||
HudMod hovered = draggingMod != null ? draggingMod : getModAt(guiMouseX, guiMouseY);
|
||||
HudMod hovered = draggingMod != null ? draggingMod : getModAt(mouseX, mouseY);
|
||||
|
||||
for (HudMod mod : hudMods) {
|
||||
ModLayout dim = mod.getDimensions();
|
||||
@@ -149,12 +151,11 @@ public class HudEditor extends SaturnScreen {
|
||||
}
|
||||
|
||||
private void drawHint(RenderScope renderScope) {
|
||||
int guiWidth = width / 2;
|
||||
int textWidth = (int) (Fonts.getWidth(HINT, Theme.getBodyFont()) * HINT_SCALE);
|
||||
int textHeight = (int) (Fonts.getHeight() * HINT_SCALE);
|
||||
int pillWidth = textWidth + 20;
|
||||
int pillHeight = textHeight + 10;
|
||||
int x = (guiWidth - pillWidth) / 2;
|
||||
int x = (width - pillWidth) / 2;
|
||||
int y = 8;
|
||||
|
||||
renderScope.drawRoundedRectangle(x, y, pillWidth, pillHeight, pillHeight / 2, 1, Theme.getPanelBg(),
|
||||
@@ -176,13 +177,11 @@ public class HudEditor extends SaturnScreen {
|
||||
}
|
||||
|
||||
private void keepOnScreen(ModLayout dim) {
|
||||
int guiWidth = width / 2;
|
||||
int guiHeight = height / 2;
|
||||
int w = right(dim) - dim.x.value;
|
||||
int h = bottom(dim) - dim.y.value;
|
||||
|
||||
dim.x.value = Math.max(0, Math.min(dim.x.value, guiWidth - w));
|
||||
dim.y.value = Math.max(0, Math.min(dim.y.value, guiHeight - h));
|
||||
dim.x.value = Math.max(0, Math.min(dim.x.value, width - w));
|
||||
dim.y.value = Math.max(0, Math.min(dim.y.value, height - h));
|
||||
}
|
||||
|
||||
private static int right(ModLayout dim) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.saturnclient.ui.screens;
|
||||
import org.saturnclient.common.provider.Providers;
|
||||
import org.saturnclient.config.AnimationConfig;
|
||||
import org.saturnclient.config.Config;
|
||||
import org.saturnclient.config.property.FloatProperty;
|
||||
import org.saturnclient.ui.SaturnScreen;
|
||||
import org.saturnclient.ui.anim.Fade;
|
||||
import org.saturnclient.ui.anim.SlideY;
|
||||
@@ -24,6 +25,11 @@ public class ShiftMenu extends SaturnScreen {
|
||||
super("Shift Menu");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FloatProperty getScaleSetting() {
|
||||
return Config.hudMenuScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
draw(new ImageTexture(Textures.LOGO_TEXT)
|
||||
|
||||
@@ -6,9 +6,11 @@ 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.property.FloatProperty;
|
||||
import org.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui.Element;
|
||||
import org.saturnclient.ui.SaturnScreen;
|
||||
import org.saturnclient.ui.anim.Animation;
|
||||
import org.saturnclient.ui.anim.Fade;
|
||||
import org.saturnclient.ui.anim.SlideFade;
|
||||
import org.saturnclient.ui.anim.SlideY;
|
||||
@@ -16,96 +18,133 @@ 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.IconButton;
|
||||
import org.saturnclient.ui.elements.ImageTexture;
|
||||
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.
|
||||
* Saturn's title screen: the logo over the game's menus in the middle, the
|
||||
* player and a quit button in the top corners, and along the bottom a dock of
|
||||
* Saturn's screens, the shift menu and Minecraft's options, 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;
|
||||
// The logo and wordmark as the shift menu has them: the wordmark over the logo's foot
|
||||
private static final int LOGO = 98;
|
||||
private static final int WORDMARK_WIDTH = 98;
|
||||
private static final int WORDMARK_HEIGHT = 10;
|
||||
private static final int WORDMARK_Y = 88;
|
||||
/** From the logo's foot to the menus, close so they read as one block */
|
||||
private static final int MENUS_GAP = 14;
|
||||
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;
|
||||
/** Space around the account chip, quit button, dock and version, from the screen's edges */
|
||||
private static final int EDGE = 14;
|
||||
/** The top corners sit a little tighter */
|
||||
private static final int TOP_EDGE = 10;
|
||||
/** As tall as the account chip beside it */
|
||||
private static final int QUIT_SIZE = AccountChip.HEIGHT;
|
||||
|
||||
// The intro comes in in reading order, left to right and top to bottom, one step apart
|
||||
private static final int STEP_ACCOUNT = 0;
|
||||
private static final int STEP_QUIT = 1;
|
||||
private static final int STEP_LOGO = 2;
|
||||
private static final int STEP_MENUS = 3;
|
||||
/** After the two menus */
|
||||
private static final int STEP_VERSION = 5;
|
||||
private static final int STEP_DOCK = 6;
|
||||
private static final float VERSION_SCALE = 0.5f;
|
||||
private static final float VERSION_ALPHA = 0.6f;
|
||||
|
||||
public TitleMenu() {
|
||||
super("Title Menu");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FloatProperty getScaleSetting() {
|
||||
return Config.titleScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
blurBackground = false;
|
||||
|
||||
// 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 menusHeight = BUTTON_HEIGHT * 2 + GAP;
|
||||
int blockHeight = LOGO + MENUS_GAP + 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)
|
||||
.animation(step(new SlideY(AnimationConfig.logo, -20), STEP_LOGO)));
|
||||
draw(new ImageTexture(Textures.LOGO_TEXT)
|
||||
.dimensions(WORDMARK_WIDTH, WORDMARK_HEIGHT)
|
||||
.position((width - WORDMARK_WIDTH) / 2, top + LOGO + 16)
|
||||
.animation(new Fade(AnimationConfig.logo.duration.value)));
|
||||
.position((width - WORDMARK_WIDTH) / 2, top + WORDMARK_Y)
|
||||
.animation(step(new Fade(AnimationConfig.logo.duration.value), STEP_LOGO)));
|
||||
|
||||
int rowHeight = BUTTON_HEIGHT + GAP;
|
||||
int halfWidth = (COLUMN_WIDTH - GAP) / 2;
|
||||
AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu);
|
||||
AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu)
|
||||
.after(stepDelay(STEP_MENUS));
|
||||
|
||||
addMenuButton(menus, new Button("Singleplayer",
|
||||
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld))
|
||||
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0));
|
||||
addMenuButton(menus, new Button("Multiplayer",
|
||||
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer))
|
||||
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, rowHeight));
|
||||
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));
|
||||
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, BUTTON_HEIGHT + GAP));
|
||||
|
||||
draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + 16 + WORDMARK_HEIGHT + 36));
|
||||
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()
|
||||
.position(EDGE, height - EDGE - AccountChip.HEIGHT)
|
||||
.animation(new Fade(AnimationConfig.mainMenu)));
|
||||
.position(TOP_EDGE, TOP_EDGE)
|
||||
.animation(step(new SlideFade(AnimationConfig.mainMenu, 8), STEP_ACCOUNT)));
|
||||
draw(new IconButton(Textures.ICON_CLOSE, () -> Providers.saturn.getClient().scheduleStop())
|
||||
.dimensions(QUIT_SIZE, QUIT_SIZE)
|
||||
.position(width - TOP_EDGE - QUIT_SIZE, TOP_EDGE)
|
||||
.animation(step(new SlideFade(AnimationConfig.mainMenu, 8), STEP_QUIT)));
|
||||
|
||||
// Along the bottom: the dock of Saturn's screens, the shift menu and Minecraft's options, and the version
|
||||
Dock dock = new Dock(List.of(
|
||||
new Dock.Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())),
|
||||
new Dock.Item(Textures.ICON_COSMETICS,
|
||||
() -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())),
|
||||
new Dock.Item(Textures.ICON_HUD_EDITOR,
|
||||
() -> Providers.saturn.getClient().setScreen(new HudEditor()))));
|
||||
new Dock.Item(Textures.LOGO, () -> Providers.saturn.getClient().setScreen(new ShiftMenu())),
|
||||
new Dock.Item(Textures.ICON_SETTINGS,
|
||||
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options))));
|
||||
draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT)
|
||||
.animation(new SlideFade(AnimationConfig.mainMenu, 12)));
|
||||
.animation(step(new SlideFade(AnimationConfig.mainMenu, -12), STEP_DOCK)));
|
||||
|
||||
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)));
|
||||
draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont,
|
||||
() -> Theme.withAlpha(VERSION_ALPHA, Theme.getMutedFg()))
|
||||
.position(EDGE, height - EDGE - versionHeight)
|
||||
.animation(step(new Fade(AnimationConfig.mainMenu), STEP_VERSION)));
|
||||
}
|
||||
|
||||
/**
|
||||
* "Saturn Client 0.1.0 · Minecraft 1.21.11", from the mod version
|
||||
* "0.1.0+1.21.11".
|
||||
* When the intro's {@code step} starts, in ms: the Main Menu stagger apart,
|
||||
* or all at once when staggering is off.
|
||||
*/
|
||||
private static int stepDelay(int step) {
|
||||
return Config.stagger.value ? step * AnimationConfig.mainMenu.stagger.value : 0;
|
||||
}
|
||||
|
||||
private static Animation step(Animation animation, int step) {
|
||||
animation.delay = stepDelay(step);
|
||||
return animation;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Saturn 0.1.0 · 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;
|
||||
String text = "Saturn " + version[0];
|
||||
return version.length > 1 ? text + " · " + version[1] : text;
|
||||
}
|
||||
|
||||
private static void addMenuButton(AnimationStagger stagger, Element button) {
|
||||
|
||||
Reference in New Issue
Block a user