Merge pull request #23 from saturnclientmc/new-ui-theme-and-animations

New UI theme and animations
This commit is contained in:
2026-02-21 18:48:24 -08:00
committed by GitHub
54 changed files with 1158 additions and 1023 deletions
@@ -2,7 +2,7 @@ package org.saturnclient.modules;
import java.util.Map; import java.util.Map;
import org.saturnclient.saturnclient.config.NamedProperty; import org.saturnclient.saturnclient.config.Property.NamedProperty;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
public class ModDimensions { public class ModDimensions {
@@ -20,17 +20,17 @@ public class ModDimensions {
public NamedProperty<Map<String, Property<?>>> prop() { public NamedProperty<Map<String, Property<?>>> prop() {
return Property.namespace(Map.of( return Property.namespace(Map.of(
"X", x, "X", x,
"Y", y, "Y", y,
"Scale", scale, "Scale", scale,
"Background Color", bgColor, "Background Color", bgColor,
"Foreground Color", fgColor, "Foreground Color", fgColor,
"Corner Radius", radius, "Corner Radius", radius,
"Font", font "Font", font)).named("In-Game Display");
)).named("In-Game Display");
} }
public ModDimensions() {} public ModDimensions() {
}
public ModDimensions(int width, int height) { public ModDimensions(int width, int height) {
this.width = width; this.width = width;
@@ -1,9 +1,9 @@
package org.saturnclient.modules; package org.saturnclient.modules;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.saturnclient.config.Property.NamedProperty;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@@ -19,8 +19,11 @@ public abstract class Module {
} }
} }
public void render(RenderScope scope) {} public void render(RenderScope scope) {
public void tick() {} }
public void tick() {
}
public abstract boolean isEnabled(); public abstract boolean isEnabled();
@@ -9,6 +9,7 @@ import net.minecraft.client.util.math.MatrixStack;
import org.saturnclient.modules.ModManager; import org.saturnclient.modules.ModManager;
import org.saturnclient.saturnclient.client.ServiceClient; import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.cosmetics.Emotes; import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.saturnclient.cosmetics.Hats; import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
@@ -40,6 +41,10 @@ public class SaturnClient implements ModInitializer {
SaturnClientConfig.init(); SaturnClientConfig.init();
ModManager.init(); ModManager.init();
client.execute(() -> {
SaturnScreen.preload(client);
});
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save()); ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> { SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> {
@@ -1,83 +0,0 @@
package org.saturnclient.saturnclient;
import org.saturnclient.saturnclient.config.Property;
import java.util.Map;
import java.util.UUID;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.ui2.resources.Textures;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
public class SaturnClientConfig {
public static ConfigManager config;
public static Property<Boolean> realisticLogo = Property.bool(false);
public static Property<Boolean> saturnTitleScreen = Property.bool(true);
public static Property<Boolean> bendyCloaks = Property.bool(true);
public static Property<Integer> openEmoteWheel = Property.keybinding(GLFW.GLFW_KEY_B);
static enum Role {
OWNER,
ADMIN,
PARTNER,
CONTRIBUTOR
}
private static final Map<UUID, Role> ROLES = Map.of(
UUID.fromString("d362a042-28bd-49e1-a807-ae74dbe8aba9"),
Role.OWNER);
public static Identifier getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
}
public static String getSaturnIndicator() {
return realisticLogo.value ? "" : "";
}
/*
* Gets the icon color of a specific individual, here are the different colors
*
* - Owner: Dark Red
* - Admin: Red
* - Partners: Gold
* - Contributor: Aqua
* - Other/player: White
*/
public static Formatting getIconColor(UUID uuid) {
Role role = ROLES.get(uuid);
if (role == null) {
return Formatting.WHITE;
}
switch (role) {
case OWNER:
return Formatting.DARK_RED;
case ADMIN:
return Formatting.RED;
case PARTNER:
return Formatting.GOLD;
case CONTRIBUTOR:
return Formatting.AQUA;
default:
return Formatting.WHITE;
}
}
public static void init() {
config = new ConfigManager("Saturn Client");
config.property("Realistic logo", realisticLogo);
config.property("Saturn client title screen", saturnTitleScreen);
config.property("Open Emote Wheel", openEmoteWheel);
config.property("Bendy Cloaks", bendyCloaks);
}
}
@@ -0,0 +1,59 @@
package org.saturnclient.saturnclient.client.player;
import java.util.Map;
import java.util.UUID;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import net.minecraft.util.Formatting;
public class Roles {
static enum Role {
OWNER,
ADMIN,
PARTNER,
CONTRIBUTOR
}
private static final Map<UUID, Role> ROLES = Map.of(
UUID.fromString("d362a042-28bd-49e1-a807-ae74dbe8aba9"),
Role.OWNER);
/*
* Gets the icon color of a specific individual, here are the different colors
*
* - Owner: Dark Red
* - Admin: Red
* - Partners: Gold
* - Contributor: Aqua
* - Other/player: White
*/
public static Formatting getIconColor(UUID uuid) {
Role role = ROLES.get(uuid);
if (role == null) {
return Formatting.WHITE;
}
switch (role) {
case OWNER:
return Formatting.DARK_RED;
case ADMIN:
return Formatting.RED;
case PARTNER:
return Formatting.GOLD;
case CONTRIBUTOR:
return Formatting.AQUA;
default:
return Formatting.WHITE;
}
}
public static String getSaturnIndicator() {
return SaturnClientConfig.realisticLogo.value ? "" : "";
}
}
@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.client.ServiceClient; import org.saturnclient.saturnclient.client.ServiceClient;
public class SaturnPlayer { public class SaturnPlayer {
@@ -22,14 +23,17 @@ public class SaturnPlayer {
this.name = name; this.name = name;
} }
@Nullable
public static SaturnPlayer get() { public static SaturnPlayer get() {
return PLAYERS.get(ServiceClient.uuid); return PLAYERS.get(ServiceClient.uuid);
} }
@Nullable
public static SaturnPlayer get(UUID uuid) { public static SaturnPlayer get(UUID uuid) {
return PLAYERS.get(uuid); return PLAYERS.get(uuid);
} }
@Nullable
public static SaturnPlayer get(String name) { public static SaturnPlayer get(String name) {
UUID uuid = PLAYER_NAMES.get(name); UUID uuid = PLAYER_NAMES.get(name);
if (uuid == null) if (uuid == null)
@@ -1,107 +1,94 @@
package org.saturnclient.saturnclient.config; package org.saturnclient.saturnclient.config;
import com.google.gson.*; import com.google.gson.*;
import org.saturnclient.saturnclient.SaturnClient;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.LinkedHashMap;
import org.saturnclient.saturnclient.SaturnClient; import java.util.Map;
public class ConfigManager { public class ConfigManager {
private static final File configFile = new File(SaturnClient.client.runDirectory, "saturn.json"); private static final File configFile = new File(SaturnClient.client.runDirectory, "saturn.json");
private static final Map<String, Map<String, Property<?>>> properties = new HashMap<>();
private static JsonObject cachedThemeJson = null; // SINGLE ROOT TREE
private static final Map<String, Property<?>> ROOT = new LinkedHashMap<>();
private static JsonObject cachedJson = null;
private final Map<String, Property<?>> currentMap; private final Map<String, Property<?>> currentMap;
private final String namespace;
private final String[] path;
/**
* Creates a root namespace
*/
public ConfigManager(String namespace) { public ConfigManager(String namespace) {
this.namespace = namespace;
this.currentMap = new LinkedHashMap<>(); this.currentMap = new LinkedHashMap<>();
properties.put(namespace, currentMap); this.path = new String[] { namespace }; // Path is just ["namespace"]
System.out.println("Created namespace: " + namespace); ROOT.put(namespace, Property.namespace(currentMap));
} }
/**
* Creates a sub-namespace inside a parent
*/
public ConfigManager(ConfigManager parent, String namespace) { public ConfigManager(ConfigManager parent, String namespace) {
this.namespace = namespace;
this.currentMap = new LinkedHashMap<>(); this.currentMap = new LinkedHashMap<>();
Property<Map<String, Property<?>>> nsProp = Property.namespace(currentMap);
parent.property(namespace, nsProp);
System.out.println("Created sub-namespace: " + namespace);
}
public void sub(String name) { // Build new path: parent path + current namespace
property(name, Property.namespace(new LinkedHashMap<>())); this.path = new String[parent.path.length + 1];
System.arraycopy(parent.path, 0, this.path, 0, parent.path.length);
this.path[this.path.length - 1] = namespace;
parent.property(namespace, Property.namespace(currentMap));
} }
/**
* Add a property
*/
public <T> Property<T> property(String name, Property<T> value) { public <T> Property<T> property(String name, Property<T> value) {
currentMap.put(name, value); currentMap.put(name, value);
loadProp(name, value); loadSingleProperty(name, value);
return value; return value;
} }
private <T> void loadProp(String name, Property<T> prop) { /**
JsonObject source = cachedThemeJson != null ? cachedThemeJson : loadAndCache(); * Add empty namespace
if (source == null) */
return; public ConfigManager sub(String name) {
return new ConfigManager(this, name);
JsonElement namespaceJson = source.get(namespace);
if (namespaceJson == null || !namespaceJson.isJsonObject())
return;
JsonElement value = namespaceJson.getAsJsonObject().get(name);
if (value != null)
prop.loadFromJson(value);
}
private static JsonObject loadAndCache() {
try {
if (!configFile.exists()) {
Files.writeString(configFile.toPath(), "{}");
return new JsonObject();
}
String json = Files.readString(configFile.toPath());
cachedThemeJson = JsonParser.parseString(json).getAsJsonObject();
return cachedThemeJson;
} catch (IOException e) {
SaturnClient.LOGGER.error("Error reading config file", e);
return null;
}
} }
/**
* Load entire config
*/
public static void load() { public static void load() {
SaturnClient.LOGGER.info("Loading config..."); SaturnClient.LOGGER.info("Loading config...");
JsonObject json = loadAndCache(); JsonObject json = loadAndCache();
if (json == null) if (json == null)
return; return;
properties.forEach((namespace, map) -> { loadRecursive(json, ROOT);
JsonElement nsElement = json.get(namespace);
if (nsElement != null && nsElement.isJsonObject()) {
loadProperties(nsElement.getAsJsonObject(), map);
}
});
}
private static void loadProperties(JsonObject json, Map<String, Property<?>> props) {
props.forEach((name, prop) -> {
JsonElement el = json.get(name);
if (el != null)
prop.loadFromJson(el);
});
} }
/**
* Save entire config
*/
public static void save() { public static void save() {
SaturnClient.LOGGER.info("Saving config..."); SaturnClient.LOGGER.info("Saving config...");
JsonObject root = new JsonObject();
properties.forEach((namespace, map) -> { JsonObject rootJson = new JsonObject();
JsonObject nsJson = new JsonObject(); saveRecursive(rootJson, ROOT);
saveProperties(nsJson, map);
root.add(namespace, nsJson);
});
try { try {
String formatted = new GsonBuilder().setPrettyPrinting().create().toJson(root); String formatted = new GsonBuilder()
.setPrettyPrinting()
.create()
.toJson(rootJson);
Files.writeString(configFile.toPath(), formatted); Files.writeString(configFile.toPath(), formatted);
SaturnClient.LOGGER.info("Config saved successfully."); SaturnClient.LOGGER.info("Config saved successfully.");
} catch (IOException e) { } catch (IOException e) {
@@ -109,8 +96,83 @@ public class ConfigManager {
} }
} }
private static void saveProperties(JsonObject json, Map<String, Property<?>> props) { /**
props.forEach((name, prop) -> json.add(name, prop.toJson())); * Recursive save
*/
private static void saveRecursive(JsonObject json, Map<String, Property<?>> map) {
map.forEach((name, prop) -> {
if (prop.isNamespace()) {
JsonObject child = new JsonObject();
saveRecursive(child, prop.getNamespaceMap());
json.add(name, child);
} else {
json.add(name, prop.toJson());
}
});
}
/**
* Recursive load
*/
private static void loadRecursive(JsonObject json, Map<String, Property<?>> map) {
map.forEach((name, prop) -> {
JsonElement element = json.get(name);
if (element == null)
return;
if (prop.isNamespace() && element.isJsonObject()) {
loadRecursive(element.getAsJsonObject(), prop.getNamespaceMap());
} else {
prop.loadFromJson(element);
}
});
}
/**
* Load only a single property immediately after creation,
* traversing the JSON tree based on this manager's path.
*/
private void loadSingleProperty(String name, Property<?> prop) {
JsonObject source = cachedJson != null ? cachedJson : loadAndCache();
if (source == null)
return;
// Traverse the tree to the correct namespace
JsonObject currentScope = source;
for (String segment : path) {
JsonElement next = currentScope.get(segment);
if (next != null && next.isJsonObject()) {
currentScope = next.getAsJsonObject();
} else {
return; // Path doesn't exist in config yet
}
}
// Now look for the property inside the correctly nested object
JsonElement element = currentScope.get(name);
if (element != null) {
prop.loadFromJson(element);
}
}
/**
* Reads file and caches json
*/
private static JsonObject loadAndCache() {
try {
if (!configFile.exists()) {
Files.writeString(configFile.toPath(), "{}");
return new JsonObject();
}
String json = Files.readString(configFile.toPath());
cachedJson = JsonParser.parseString(json).getAsJsonObject();
return cachedJson;
} catch (IOException e) {
SaturnClient.LOGGER.error("Error reading config file", e);
return null;
}
} }
public Map<String, Property<?>> getProperties() { public Map<String, Property<?>> getProperties() {
@@ -1,11 +0,0 @@
package org.saturnclient.saturnclient.config;
public class NamedProperty<T> {
public String name;
public Property<T> prop;
public NamedProperty(String name, Property<T> value) {
this.name = name;
this.prop = value;
}
}
@@ -10,6 +10,15 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
public class Property<T> { public class Property<T> {
public static class NamedProperty<T> {
public String name;
public Property<T> prop;
public NamedProperty(String name, Property<T> value) {
this.name = name;
this.prop = value;
}
}
public enum PropertyType { public enum PropertyType {
BOOLEAN, BOOLEAN,
@@ -277,4 +286,13 @@ public class Property<T> {
if (value != null) if (value != null)
this.loadFromJson(value); this.loadFromJson(value);
} }
public boolean isNamespace() {
return type == PropertyType.NAMESPACE;
}
@SuppressWarnings("unchecked")
public Map<String, Property<?>> getNamespaceMap() {
return isNamespace() ? (Map<String, Property<?>>) (Object) value : null;
}
} }
@@ -0,0 +1,32 @@
package org.saturnclient.saturnclient.config;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.ui2.resources.Textures;
import net.minecraft.util.Identifier;
public class SaturnClientConfig {
public static ConfigManager config;
public static Property<Boolean> realisticLogo = Property.bool(false);
public static Property<Boolean> saturnTitleScreen = Property.bool(true);
public static Property<Boolean> bendyCloaks = Property.bool(true);
public static Property<Integer> openEmoteWheel = Property.keybinding(GLFW.GLFW_KEY_B);
public static Property<Boolean> stagger = Property.bool(true);
public static Identifier getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
}
public static void init() {
config = new ConfigManager("Saturn Client");
config.property("Realistic logo", realisticLogo);
config.property("Saturn client title screen", saturnTitleScreen);
config.property("Open Emote Wheel", openEmoteWheel);
config.property("Bendy Cloaks", bendyCloaks);
config.property("Stagger Animations", stagger);
// Initialize a sub namespace for theme
Theme.init(config);
}
}
@@ -0,0 +1,88 @@
package org.saturnclient.saturnclient.config;
public class Theme {
// Main UI background (slightly warm dark to match Saturn tone)
public static final Property<Integer> BACKGROUND = Property.color(0xFF12100D);
// Default text color
public static final Property<Integer> FOREGROUND = Property.color(0xFFF2E6D2);
// Widget default background (normal state)
public static final Property<Integer> PRIMARY = Property.color(0xFF1C1915);
// Widget text color
public static final Property<Integer> PRIMARY_FG = Property.color(0xFFEAD9B8);
// Saturn brand color (hovered / enabled / selected)
public static final Property<Integer> ACCENT = Property.color(0xFFE2BF7D);
// Text/icons shown on accent background
public static final Property<Integer> ACCENT_FG = Property.color(0xFF1A140A);
// Misc colors
public static final Property<Integer> SCROLL = Property.color(0xFFE2BF7D);
// UI metrics
public static final Property<Integer> BG_RADIUS = Property.integer(12);
public static final Property<Integer> WIDGET_RADIUS = Property.integer(10);
// Font (keeping your current system)
public static Property<Integer> FONT = Property.font(1);
public static void init(ConfigManager parent) {
ConfigManager 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 Radius", BG_RADIUS);
config.property("Widget Radius", WIDGET_RADIUS);
config.property("Font", FONT);
}
/**
* primary by default, accent if true
*/
public static int getBg(boolean hover) {
return hover ? ACCENT.value : PRIMARY.value;
}
/**
* (Foreground) primary by default, accent if true
*/
public static int getFg(boolean hover) {
return hover ? ACCENT_FG.value : PRIMARY_FG.value;
}
/**
* Multiplies existing alpha with 0–255 alpha.
*/
public static int withAlpha(int alpha, int color) {
alpha = Math.max(0, Math.min(255, alpha));
int originalAlpha = (color >>> 24) & 0xFF;
// Merge (multiply) alphas
int merged = (originalAlpha * alpha) / 255;
return (color & 0x00FFFFFF) | (merged << 24);
}
/**
* Multiplies existing alpha with 0.0–1.0 alpha.
*/
public static int withAlpha(float alpha, int color) {
alpha = Math.max(0f, Math.min(1f, alpha));
int originalAlpha = (color >>> 24) & 0xFF;
int merged = (int) (originalAlpha * alpha);
return (color & 0x00FFFFFF) | (merged << 24);
}
}
@@ -1,93 +0,0 @@
package org.saturnclient.saturnclient.config;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.saturnclient.saturnclient.SaturnClient;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class ThemeManager {
private static File themeFile = new File(
SaturnClient.client.runDirectory,
"saturn.theme.json");
private static List<ThemeManager> theme = new ArrayList<>();
private static JsonObject cachedThemeJson = null;
private Map<String, Map<String, Object>> states;
private Map<String, Property<?>> currentStyling;
private String namespace;
public ThemeManager(String namespace, String... states) {
this.namespace = namespace;
this.states = new LinkedHashMap<>();
this.currentStyling = new LinkedHashMap<>();
this.states.put(null, new LinkedHashMap<>());
for (String s : states) {
this.states.put(s, new LinkedHashMap<>());
}
theme.add(this);
}
public void setState(String state) {
for (Map.Entry<String, Object> tProp : states.get(state).entrySet()) {
currentStyling.get(tProp.getKey()).setValue(tProp.getValue());
}
}
public void applyState(String state) {
for (Map.Entry<String, Object> tProp : states.get(state).entrySet()) {
if (!states.get(null).get(tProp.getKey()).equals(tProp.getValue()))
currentStyling.get(tProp.getKey()).setValue(tProp.getValue());
}
}
public <T> Property<T> property(String name, Property<T> prop) {
for (Map.Entry<String, Map<String, Object>> state : this.states.entrySet()) {
Property<?> property = prop.copy();
if (cachedThemeJson == null) {
load();
}
String stateName = state.getKey();
property.load(name, cachedThemeJson.get(stateName != null ? namespace + "@" + stateName : namespace));
state.getValue().put(name, property.value);
}
currentStyling.put(name, prop);
return prop;
}
public <T> void propertyStateDefault(String state, String name, T value) {
Property<?> prop = currentStyling.get(name).copy();
prop.setValue(value, value);
if (cachedThemeJson == null) {
load();
}
prop.load(name, cachedThemeJson.get(state != null ? namespace + "@" + state : namespace));
states.get(state).put(name, prop.value);
}
public static void load() {
try {
SaturnClient.LOGGER.info("Starting to load theme...");
if (!themeFile.exists()) {
themeFile.createNewFile();
Files.write(themeFile.toPath(), "{}".getBytes());
}
cachedThemeJson = JsonParser.parseString(
new String(Files.readAllBytes(themeFile.toPath()))).getAsJsonObject();
SaturnClient.LOGGER.info("Theme loaded");
} catch (IOException e) {
SaturnClient.LOGGER.error("Error reading the theme file", e);
}
}
}
@@ -1,7 +1,7 @@
package org.saturnclient.saturnclient.cosmetics; package org.saturnclient.saturnclient.cosmetics;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.client.player.SaturnPlayer; import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.OverlayTexture;
@@ -283,14 +283,16 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light,
PlayerEntityRenderState playerEntityRenderState, float f, float g) { PlayerEntityRenderState playerEntityRenderState, float f, float g) {
if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible) { if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible
|| playerEntityRenderState.skinTextures.capeTexture() != null) {
return; return;
} }
if (playerEntityRenderState.skinTextures.capeTexture() != null) { SaturnPlayer player = SaturnPlayer.get(playerEntityRenderState.name);
if (player == null) {
return; return;
} }
SaturnPlayer player = SaturnPlayer.get(playerEntityRenderState.name);
Identifier customCape = Cloaks.getCurrentCloakTexture(player.uuid); Identifier customCape = Cloaks.getCurrentCloakTexture(player.uuid);
if (customCape == null || this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack, if (customCape == null || this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack,
@@ -6,8 +6,8 @@ import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.InputUtil;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.client.ServiceClient; import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.ui2.screens.EmoteWheel; import org.saturnclient.ui2.screens.EmoteWheel;
import org.saturnclient.ui2.screens.ShiftMenu; import org.saturnclient.ui2.screens.ShiftMenu;
@@ -14,7 +14,7 @@ import net.minecraft.text.Text;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.client.player.Roles;
import org.saturnclient.saturnclient.client.player.SaturnPlayer; import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import java.util.UUID; import java.util.UUID;
@@ -43,8 +43,8 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
UUID uuid = isSaturn(state); UUID uuid = isSaturn(state);
if (uuid != null) { if (uuid != null) {
MutableText iconText = Text.literal(SaturnClientConfig.getSaturnIndicator()) MutableText iconText = Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid))); .styled(style -> style.withColor(Roles.getIconColor(uuid)));
Text nameText = text.copy().styled(style -> style.withColor(Formatting.WHITE)); Text nameText = text.copy().styled(style -> style.withColor(Formatting.WHITE));
@@ -59,7 +59,9 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
if (state instanceof PlayerEntityRenderState) { if (state instanceof PlayerEntityRenderState) {
String name = ((PlayerEntityRenderState) state).name; String name = ((PlayerEntityRenderState) state).name;
SaturnPlayer player = SaturnPlayer.get(name); SaturnPlayer player = SaturnPlayer.get(name);
return player.uuid; if (player != null) {
return player.uuid;
}
} }
return null; return null;
@@ -1,6 +1,6 @@
package org.saturnclient.saturnclient.mixin; package org.saturnclient.saturnclient.mixin;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.ui2.screens.TitleMenu; import org.saturnclient.ui2.screens.TitleMenu;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -3,6 +3,7 @@ package org.saturnclient.saturnclient.mixin;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -40,11 +41,8 @@ public abstract class SplashOverlayMixin {
@Shadow(remap = false) @Shadow(remap = false)
private float progress; private float progress;
private static int withAlpha(int color, int alpha) {
return color & 0xFFFFFF | alpha << 24;
}
/** /**
* @author SaturnClient
* @reason Replace vanilla splash rendering completely but keep exit logic * @reason Replace vanilla splash rendering completely but keep exit logic
*/ */
@Overwrite @Overwrite
@@ -80,19 +78,17 @@ public abstract class SplashOverlayMixin {
if (fadeOutProgress >= 1.0F) { if (fadeOutProgress >= 1.0F) {
renderCurrentScreen(context, mouseX, mouseY, delta); renderCurrentScreen(context, mouseX, mouseY, delta);
int overlayAlpha = computeOverlayAlpha(fadeOutProgress - 1f); int overlayAlpha = computeOverlayAlpha(fadeOutProgress - 1f);
// context.fill(0, 0, screenWidth, screenHeight,
// withAlpha(0x53389E, overlayAlpha));
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight, scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
withAlpha(0xFFFFFF, overlayAlpha)); Theme.withAlpha(overlayAlpha, 0xFFFFFFFF));
} else if (reloading) { } else if (reloading) {
if (reloadProgress < 1.0F) { if (reloadProgress < 1.0F) {
renderCurrentScreen(context, mouseX, mouseY, delta); renderCurrentScreen(context, mouseX, mouseY, delta);
} }
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight, scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
withAlpha(0xFFFFFF, alpha)); Theme.withAlpha(alpha, 0xFFFFFFFF));
} else { } else {
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight, scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
withAlpha(0xFFFFFF, alpha)); Theme.withAlpha(alpha, 0xFFFFFFFF));
} }
float u = this.reload.getProgress(); float u = this.reload.getProgress();
@@ -120,18 +116,19 @@ public abstract class SplashOverlayMixin {
/** Draw the main logo centered */ /** Draw the main logo centered */
private void drawLogo(RenderScope scope, int screenWidth, int screenHeight, int alpha) { private void drawLogo(RenderScope scope, int screenWidth, int screenHeight, int alpha) {
scope.drawTexture(Textures.LOGO, (screenWidth - 60) / 2, ((screenHeight - 60) / 2) - 20, 0, 0, 60, 60, scope.drawTexture(Textures.LOGO, (screenWidth - 60) / 2, ((screenHeight - 60) / 2) - 20, 0, 0, 60, 60,
withAlpha(0xFFFFFF, alpha)); Theme.withAlpha(alpha, 0xFFFFFFFF));
} }
private void renderProgressBar(RenderScope scope, DrawContext context, int screenWidth, int screenHeight, private void renderProgressBar(RenderScope scope, DrawContext context, int screenWidth, int screenHeight,
int alpha) { int alpha) {
int progressY = ((screenHeight - 5) / 2) + 25; int progressY = ((screenHeight - 5) / 2) + 25;
context.drawBorder((screenWidth - 120) / 2, progressY, 120, 5, withAlpha(0xFFFFFF, alpha)); context.drawBorder((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF));
int progressWidth = Math.min((int) (progress * 120), 120); int progressWidth = Math.min((int) (progress * 120), 120);
int progressX = (screenWidth - 120) / 2; int progressX = (screenWidth - 120) / 2;
context.fill(progressX, progressY, progressX + progressWidth, progressY + 5, withAlpha(0xFFFFFF, alpha)); context.fill(progressX, progressY, progressX + progressWidth, progressY + 5,
Theme.withAlpha(alpha, 0xFFFFFFFF));
} }
/** Compute overlay alpha from fade progress */ /** Compute overlay alpha from fade progress */
@@ -7,7 +7,7 @@ import net.minecraft.util.Formatting;
import java.util.UUID; import java.util.UUID;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.client.player.Roles;
import org.saturnclient.saturnclient.client.player.SaturnPlayer; import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
@@ -34,8 +34,8 @@ public abstract class TabListMixin {
UUID uuid = profile.getId(); UUID uuid = profile.getId();
if (SaturnPlayer.get(uuid) != null) { if (SaturnPlayer.get(uuid) != null) {
return Text.literal(SaturnClientConfig.getSaturnIndicator()) return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid))) .styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE))); .append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
} }
return Text.literal(name); return Text.literal(name);
@@ -32,6 +32,22 @@ public class Element {
public void charTyped(char typedChar) { public void charTyped(char typedChar) {
} }
public void mouseClicked(double mouseX, double mouseY, int button) {
}
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
}
public void mouseReleased(double mouseX, double mouseY, int button) {
}
public void playAnimationFrame(long elapsed) {
if (this.animation != null && elapsed >= this.animation.delay) {
this.animation.tick(this.curve
.apply(Math.min(1.0, (double) (elapsed - this.animation.delay) / this.animation.duration)), this);
}
}
public Element dimensions(int width, int height) { public Element dimensions(int width, int height) {
this.width = width; this.width = width;
this.height = height; this.height = height;
@@ -4,13 +4,15 @@ public class ElementContext {
public int mouseX, mouseY; public int mouseX, mouseY;
public int elementWidth, elementHeight; public int elementWidth, elementHeight;
public float elementScale; public float elementScale;
public long elapsed;
public ElementContext(int mouseX, int mouseY, Element e) { public ElementContext(long elapsed, int mouseX, int mouseY, Element e) {
this.mouseX = mouseX - e.x; this.mouseX = mouseX - e.x;
this.mouseY = mouseY - e.y; this.mouseY = mouseY - e.y;
this.elementWidth = e.width; this.elementWidth = e.width;
this.elementHeight = e.height; this.elementHeight = e.height;
this.elementScale = e.scale; this.elementScale = e.scale;
this.elapsed = elapsed;
} }
public boolean isHovering() { public boolean isHovering() {
@@ -398,4 +398,45 @@ public class RenderScope {
sprite.getMinV(), sprite.getMaxV(), color); sprite.getMinV(), sprite.getMaxV(), color);
} }
} }
public void fill(int x1, int y1, int x2, int y2, int color) {
this.fill(x1, y1, x2, y2, 0, color);
}
public void fill(int x1, int y1, int x2, int y2, int z, int color) {
this.fill(RenderLayer.getGui(), x1, y1, x2, y2, z, color);
}
public void fill(RenderLayer layer, int x1, int y1, int x2, int y2, int color) {
this.fill(layer, x1, y1, x2, y2, 0, color);
}
public void fill(RenderLayer layer, int x1, int y1, int x2, int y2, int z, int color) {
Matrix4f matrix4f = this.matrices.peek().getPositionMatrix();
int i;
if (x1 < x2) {
i = x1;
x1 = x2;
x2 = i;
}
if (y1 < y2) {
i = y1;
y1 = y2;
y2 = i;
}
VertexConsumer vertexConsumer = this.vertexConsumers.getBuffer(layer);
vertexConsumer.vertex(matrix4f, (float) x1, (float) y1, (float) z).color(color);
vertexConsumer.vertex(matrix4f, (float) x1, (float) y2, (float) z).color(color);
vertexConsumer.vertex(matrix4f, (float) x2, (float) y2, (float) z).color(color);
vertexConsumer.vertex(matrix4f, (float) x2, (float) y1, (float) z).color(color);
}
public void drawBorder(int x, int y, int width, int height, int color) {
this.fill(x, y, x + width, y + 1, color);
this.fill(x, y + height - 1, x + width, y + height, color);
this.fill(x, y + 1, x + 1, y + height - 1, color);
this.fill(x + width - 1, y + 1, x + width, y + height - 1, color);
}
} }
@@ -1,11 +1,11 @@
package org.saturnclient.ui2; package org.saturnclient.ui2;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor; import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.anim.Animation;
import org.saturnclient.ui2.anim.SlideXAbsolute;
import org.saturnclient.ui2.components.ElementRenderer; import org.saturnclient.ui2.components.ElementRenderer;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -15,6 +15,8 @@ import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.RotatingCubeMapRenderer; import net.minecraft.client.gui.RotatingCubeMapRenderer;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.DefaultFramebufferSet; import net.minecraft.client.render.DefaultFramebufferSet;
import net.minecraft.client.texture.ResourceTexture;
import net.minecraft.client.texture.TextureManager;
import net.minecraft.client.util.Pool; import net.minecraft.client.util.Pool;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@@ -23,12 +25,12 @@ public abstract class SaturnScreen extends Screen {
private final Pool pool = new Pool(3); private final Pool pool = new Pool(3);
protected List<Element> elements = new ArrayList<>(); protected List<Element> elements = new ArrayList<>();
protected float backgroundOpacity = 1.0f; protected float backgroundOpacity = 1.0f;
public int blurDuration = 700; protected Instant start = null;
public float blurProgress = 0.0f;
public int backgroundBlur = 10; public int backgroundBlur = 10;
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer( protected static final Identifier PANORAMA = Identifier.of("saturnclient",
Identifier.of("saturnclient", "textures/gui/title/background/panorama")); "textures/gui/title/background/panorama");
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(PANORAMA);
public static final RotatingCubeMapRenderer ROTATING_PANORAMA_RENDERER; public static final RotatingCubeMapRenderer ROTATING_PANORAMA_RENDERER;
@@ -36,41 +38,24 @@ public abstract class SaturnScreen extends Screen {
ROTATING_PANORAMA_RENDERER = new RotatingCubeMapRenderer(PANORAMA_RENDERER); ROTATING_PANORAMA_RENDERER = new RotatingCubeMapRenderer(PANORAMA_RENDERER);
} }
public static void preload(MinecraftClient client) {
TextureManager textureManager = client.getTextureManager();
// Force upload each texture to GPU
for (int i = 0; i < 6; i++) {
String var10001 = PANORAMA.getPath();
Identifier tex = PANORAMA.withPath(var10001 + "_" + i + ".png");
ResourceTexture resourceTexture = new ResourceTexture(tex);
textureManager.registerTexture(tex, resourceTexture);
}
}
public SaturnScreen(String title) { public SaturnScreen(String title) {
super(Text.literal(title)); super(Text.literal(title));
} }
public void draw(Element element) { public void draw(Element element) {
synchronized (elements) { ElementRenderer.draw(elements, element);
elements.add(element);
}
if (element.animation != null) {
element.animation.init(element);
Animation.execute((Float progress) -> {
element.animation.tick(progress, element);
}, element.animation.duration);
}
if (element.duration != null) {
new Thread(() -> {
try {
Animation fadeOut = new SlideXAbsolute(700, -(width - element.x));
Thread.sleep(element.duration);
fadeOut.init(element);
Animation.executeSync((Float progress) -> {
fadeOut.tick(progress, element);
}, fadeOut.duration);
synchronized (elements) {
elements.remove(element);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
} }
@Override @Override
@@ -79,10 +64,6 @@ public abstract class SaturnScreen extends Screen {
width *= 2; width *= 2;
height *= 2; height *= 2;
ui(); // abstraction to render the saturn ui and also render extra stuff here ui(); // abstraction to render the saturn ui and also render extra stuff here
Animation.execute((Float progress) -> {
blurProgress = progress;
}, blurDuration);
} }
public abstract void ui(); public abstract void ui();
@@ -90,6 +71,11 @@ public abstract class SaturnScreen extends Screen {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) { public void render(DrawContext context, int mouseX, int mouseY, float delta) {
if (start == null) {
start = Instant.now();
}
long elapsed = Duration.between(start, Instant.now()).toMillis();
mouseX *= 2; mouseX *= 2;
mouseY *= 2; mouseY *= 2;
@@ -100,8 +86,9 @@ public abstract class SaturnScreen extends Screen {
PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect( PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
Identifier.ofVanilla("blur"), Identifier.ofVanilla("blur"),
DefaultFramebufferSet.MAIN_ONLY); DefaultFramebufferSet.MAIN_ONLY);
if (postEffectProcessor != null) { if (postEffectProcessor != null) {
postEffectProcessor.setUniforms("Radius", backgroundBlur * blurProgress); postEffectProcessor.setUniforms("Radius", backgroundBlur * Math.min((float) elapsed / 700, 1.0f));
postEffectProcessor.render(this.client.getFramebuffer(), this.pool); postEffectProcessor.render(this.client.getFramebuffer(), this.pool);
} }
@@ -114,7 +101,7 @@ public abstract class SaturnScreen extends Screen {
renderScope.matrices.scale(0.5f, 0.5f, 0.5f); renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
ElementRenderer.render(new ArrayList<>(elements), renderScope, mouseX, mouseY); ElementRenderer.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY);
renderScope.matrices.pop(); renderScope.matrices.pop();
} }
@@ -129,6 +116,26 @@ public abstract class SaturnScreen extends Screen {
return false; return false;
} }
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.mouseDragged(elements, mouseX, mouseY, button, deltaX, deltaY);
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.mouseReleased(elements, mouseX, mouseY, button);
return super.mouseReleased(mouseX, mouseY, button);
}
@Override @Override
public boolean mouseScrolled( public boolean mouseScrolled(
double mouseX, double mouseX,
@@ -1,75 +1,16 @@
package org.saturnclient.ui2.anim; package org.saturnclient.ui2.anim;
import java.util.function.Consumer;
import java.util.function.Function;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
public abstract class Animation { public abstract class Animation {
public int duration; public int duration;
public int delay = 0;
Animation(int duration) { Animation(int duration) {
this.duration = duration; this.duration = duration;
} }
public abstract void tick(float progress, Element element); public abstract void tick(double progress, Element element);
public abstract void init(Element element); public abstract void init(Element element);
public static void execute(Consumer<Float> tick, int duration) {
new Thread(() -> {
Function<Double, Double> curveFunction = Curve::easeInOutCubic;
float progress = 0.0f;
double incremental = (1000 - duration) / 10000.0;
for (int i = 0; i <= 100; i++) {
float newProgress = (float) (double) curveFunction.apply(i / 100.0);
while (Math.abs(newProgress - progress) > 1e-6) {
if (progress < newProgress) {
progress += incremental;
if (progress > newProgress) progress = newProgress;
} else {
progress -= incremental;
if (progress < newProgress) progress = newProgress;
}
tick.accept(newProgress);
try {
Thread.sleep(duration / 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
public static void executeSync(Consumer<Float> tick, int duration) {
Function<Double, Double> curveFunction = Curve::easeInOutCubic;
float progress = 0.0f;
double incremental = (1000 - duration) / 10000.0;
for (int i = 0; i <= 100; i++) {
float newProgress = (float) (double) curveFunction.apply(i / 100.0);
while (Math.abs(newProgress - progress) > 1e-6) {
if (progress < newProgress) {
progress += incremental;
if (progress > newProgress) progress = newProgress;
} else {
progress -= incremental;
if (progress < newProgress) progress = newProgress;
}
tick.accept(newProgress);
try {
Thread.sleep(duration / 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} }
@@ -10,13 +10,13 @@ public class Fade extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.opacity = targetOpacity * progress; element.opacity = Math.max(0.1f, targetOpacity * (float) progress);
} }
@Override @Override
public void init(Element element) { public void init(Element element) {
targetOpacity = element.opacity; targetOpacity = element.opacity;
element.opacity = 0; element.opacity = 0.1f;
} }
} }
@@ -10,8 +10,8 @@ public class FadeOut extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.opacity = targetOpacity - progress; element.opacity = targetOpacity - (float) progress;
} }
@Override @Override
@@ -0,0 +1,32 @@
package org.saturnclient.ui2.anim;
import org.saturnclient.ui2.Element;
public class SlideFade extends Animation {
public Fade fade;
public SlideY slideY;
public SlideFade(int duration, int offset) {
super(duration);
this.fade = new Fade(duration);
this.slideY = new SlideY(duration, offset);
}
@Override
public void tick(double progress, Element element) {
fade.delay = this.delay;
fade.duration = this.duration;
slideY.delay = this.delay;
slideY.duration = this.duration;
fade.tick(progress, element);
slideY.tick(progress, element);
}
@Override
public void init(Element element) {
fade.init(element);
slideY.init(element);
}
}
@@ -24,7 +24,7 @@ public class SlideX extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.x = elementX + (int) (offset * progress); element.x = elementX + (int) (offset * progress);
} }
@@ -24,7 +24,7 @@ public class SlideXAbsolute extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void tick(double progress, Element element) {
element.x = elementX + (int) (offset * progress); element.x = elementX + (int) (offset * progress);
} }
@@ -5,7 +5,7 @@ import org.saturnclient.ui2.Element;
public class SlideY extends Animation { public class SlideY extends Animation {
int offset = 20; int offset = 20;
int maxOffset = 20; int maxOffset = 20;
int elementY; double startY;
public SlideY(int duration) { public SlideY(int duration) {
super(duration); super(duration);
@@ -26,13 +26,14 @@ public class SlideY extends Animation {
} }
@Override @Override
public void tick(float progress, Element element) { public void init(Element element) {
element.y = elementY + (int) (offset * progress); startY = element.y - offset;
element.y = (int) startY;
} }
@Override @Override
public void init(Element element) { public void tick(double progress, Element element) {
element.y -= offset; double currentY = startY + offset * progress;
elementY = element.y; element.y = (int) currentY;
} }
} }
@@ -1,7 +1,6 @@
package org.saturnclient.ui2.components; package org.saturnclient.ui2.components;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -10,13 +9,7 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class CosmeticPreview extends Element { public class CosmeticPreview extends Element {
private static ThemeManager theme = new ThemeManager("CosmeticPreview", "selected", "hovering"); static int padding = 14;
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x89000000));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
private static Property<Integer> padding = theme.property("padding", Property.integer(14));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
static {}
Identifier sprite; Identifier sprite;
private Runnable onClick; private Runnable onClick;
@@ -33,22 +26,16 @@ public class CosmeticPreview extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) { renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
theme.setState("hovering"); isSelected ? Theme.ACCENT.value : Theme.PRIMARY.value);
} else if (isSelected) {
theme.setState("selected");
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding.value; int texWidth = width - padding;
int texHeight = height - padding.value; int texHeight = height - padding;
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value); renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
Theme.FOREGROUND.value);
theme.setState(null);
} }
@Override @Override
@@ -7,7 +7,6 @@ import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils; import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.anim.Animation;
public class ElementRenderer { public class ElementRenderer {
public static void draw(List<Element> elements, Element element) { public static void draw(List<Element> elements, Element element) {
@@ -17,20 +16,18 @@ public class ElementRenderer {
if (element.animation != null) { if (element.animation != null) {
element.animation.init(element); element.animation.init(element);
Animation.execute((Float progress) -> {
element.animation.tick(progress, element);
}, element.animation.duration);
} }
} }
public static void render(List<Element> elements, RenderScope renderScope, int mouseX, int mouseY) { public static void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
for (Element element : elements) { for (Element element : elements) {
element.playAnimationFrame(elapsed);
renderScope.matrices.push(); renderScope.matrices.push();
renderScope.setOpacity(element.opacity); renderScope.setOpacity(element.opacity);
renderScope.matrices.translate(element.x, element.y, 0); renderScope.matrices.translate(element.x, element.y, 0);
renderScope.matrices.scale(element.scale, element.scale, 1.0f); renderScope.matrices.scale(element.scale, element.scale, 1.0f);
element.render(renderScope, new ElementContext(mouseX, mouseY, element)); element.render(renderScope, new ElementContext(elapsed, mouseX, mouseY, element));
renderScope.matrices.pop(); renderScope.matrices.pop();
renderScope.setRenderLayer(null); renderScope.setRenderLayer(null);
} }
@@ -44,7 +41,10 @@ public class ElementRenderer {
int adjustedMouseX = (int) mouseX - element.x; int adjustedMouseX = (int) mouseX - element.x;
int adjustedMouseY = (int) mouseY - element.y; int adjustedMouseY = (int) mouseY - element.y;
if (element.opacity > 0 && Utils.isHovering(adjustedMouseX, adjustedMouseY, element.width, element.height, element.scale)) { element.mouseClicked(adjustedMouseX * element.scale, adjustedMouseY * element.scale, button);
if (element.opacity > 0 && Utils.isHovering(adjustedMouseX, adjustedMouseY, element.width,
element.height, element.scale)) {
element.focused = true; element.focused = true;
element.click((int) (adjustedMouseX * element.scale), (int) (adjustedMouseY * element.scale)); element.click((int) (adjustedMouseX * element.scale), (int) (adjustedMouseY * element.scale));
} }
@@ -58,16 +58,17 @@ public class ElementRenderer {
double mouseY, double mouseY,
double horizontalAmount, double horizontalAmount,
double verticalAmount) { double verticalAmount) {
for (Element element : new ArrayList<>(elements)) { for (Element element : new ArrayList<>(elements)) {
if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height, element.scale)) { if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height,
element.scroll( element.scale)) {
(int) mouseX - element.x, element.scroll(
(int) mouseY - element.y, (int) mouseX - element.x,
horizontalAmount, (int) mouseY - element.y,
verticalAmount); horizontalAmount,
} verticalAmount);
}
} }
}
}
public static void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) { public static void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) {
for (Element element : new ArrayList<>(elements)) { for (Element element : new ArrayList<>(elements)) {
@@ -81,4 +82,17 @@ public class ElementRenderer {
} }
} }
} }
public static void mouseDragged(List<Element> elements, double mouseX, double mouseY, int button, double deltaX,
double deltaY) {
for (Element element : new ArrayList<>(elements)) {
element.mouseDragged(mouseX - element.x, mouseY - element.y, button, deltaX, deltaY);
}
}
public static void mouseReleased(List<Element> elements, double mouseX, double mouseY, int button) {
for (Element element : new ArrayList<>(elements)) {
element.mouseReleased(mouseX - element.x, mouseY - element.y, button);
}
}
} }
@@ -2,8 +2,7 @@ package org.saturnclient.ui2.components;
import org.saturnclient.modules.Module; import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
@@ -15,23 +14,12 @@ import org.saturnclient.ui2.screens.ConfigEditor;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
public class SaturnModule extends Element { public class SaturnModule extends Element {
private static ThemeManager theme = new ThemeManager("Module", "enabled", "hovering");
private static Property<Integer> bgColor = theme.property("bg", Property.color(0xFF1d202d));
private static Property<Integer> fgColor = theme.property("fg", Property.color(0xFFA1A2B8));
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFA1A2B8));
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0xFF2e3248));
private static Property<Integer> tagBg = theme.property("tag-bg", Property.color(0xFF2e3248));
private static Property<Integer> iconRadius = theme.property("icon-radius", Property.color(10));
private static Property<Integer> font = theme.property("font", Property.font(1));
private static Property<Integer> radius = theme.property("radius", Property.color(10));
static { private final Module mod;
theme.propertyStateDefault("enabled", "bg", 0xFF1d202d);
theme.propertyStateDefault("enabled", "icon-bg", 0xFF845eee);
theme.propertyStateDefault("hovering", "fg", 0xFF845eee);
}
private Module mod; static int p = 10; // padding
static int s = 14; // settings icon size
static int expand = 5; // settings icon click area
public SaturnModule(Module mod) { public SaturnModule(Module mod) {
this.mod = mod; this.mod = mod;
@@ -41,60 +29,137 @@ public class SaturnModule extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
int p = 10; boolean hovered = ctx.isHovering();
int s = 14; boolean enabled = mod.isEnabled();
boolean settingsHover = false;
int iconX = width - s - p;
int iconY = height - s - p;
boolean settingsHover = ctx.isHovering(
getSettingsX(),
getSettingsY(),
getSettingsHitBox(),
getSettingsHitBox());
renderScope.setRenderLayer(RenderLayer::getGuiTextured); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
if (mod.isEnabled()) { // Resolve theme colors
theme.setState("enabled"); int bgColor = Theme.PRIMARY.value;
} int fgColor = hovered && !settingsHover ? Theme.ACCENT.value : Theme.PRIMARY_FG.value;
if (ctx.isHovering(width - s - p - 5, height - s - p - 5, s + 10, s + 10)) { int iconBg = enabled ? Theme.ACCENT.value : Theme.PRIMARY.value;
settingsHover = true; int iconFg = enabled ? Theme.ACCENT_FG.value : fgColor;
} else if (ctx.isHovering()) {
theme.applyState("hovering");
}
renderScope.drawRoundedRectangle(0, 0, width, height, radius.value, bgColor.value); int settingsColor = settingsHover
? Theme.ACCENT.value
: Theme.withAlpha(160, fgColor);
int h = height-(p * 2); int h = height - (p * 2);
renderScope.drawRoundedRectangle(p, p, h, h, iconRadius.value, iconBg.value); // Main background
renderScope.drawRoundedRectangle(
0, 0,
width, height,
Theme.WIDGET_RADIUS.value,
bgColor);
renderScope.drawTexture(mod.getIconTexture(), p + (p / 2), p + (p / 2), 0, 0, h - p, h - p, iconFg.value); // Icon background
renderScope.drawRoundedRectangle(
p, p,
h, h,
Theme.WIDGET_RADIUS.value,
iconBg);
renderScope.drawText(0.6f, mod.getName(), p+h+4, p + 1, font.value, fgColor.value); // Icon
renderScope.drawTexture(
mod.getIconTexture(),
p + (p / 2),
p + (p / 2),
0, 0,
h - p,
h - p,
iconFg);
renderScope.drawText(0.35f, mod.getVersion(), width - p - (int) (Fonts.getWidth(mod.getVersion(), font.value) * 0.35f), p + 1, font.value, fgColor.value); // Module name
renderScope.drawText(
0.6f,
mod.getName(),
p + h + 4,
p + 1,
Theme.FONT.value,
fgColor);
// Version (right aligned)
String version = mod.getVersion();
int versionWidth = (int) (Fonts.getWidth(version, Theme.FONT.value) * 0.35f);
renderScope.drawText(
0.35f,
version,
width - p - versionWidth,
p + 1,
Theme.FONT.value,
fgColor);
// Tags
int xt = p + h + 4; int xt = p + h + 4;
for (String tag : mod.getTags()) { for (String tag : mod.getTags()) {
int xtb = (int) (Fonts.getWidth(tag, font.value) * 0.4f); int tagWidth = (int) (Fonts.getWidth(tag, Theme.FONT.value) * 0.4f);
renderScope.drawRoundedRectangle(xt, p + (h/2), xtb + 6, 12, 5, tagBg.value);
renderScope.drawText(0.4f, tag, xt+2, p + (h/2) + 3, font.value, fgColor.value); renderScope.drawRoundedRectangle(
xt += xtb + 9; xt,
p + (h / 2),
tagWidth + 6,
12,
5,
Theme.ACCENT.value);
renderScope.drawText(
0.4f,
tag,
xt + 2,
p + (h / 2) + 3,
Theme.FONT.value,
Theme.ACCENT_FG.value);
xt += tagWidth + 9;
} }
if (settingsHover) { // Settings icon
theme.setState("hovering"); renderScope.drawTexture(
} Textures.SETTINGS,
iconX,
renderScope.drawTexture(Textures.SETTINGS, width - s - p, height - s - p, 0, 0, s, s, fgColor.value); iconY,
0, 0,
theme.setState(null); s, s,
settingsColor);
} }
@Override @Override
public void click(int mouseX, int mouseY) { public void click(int mouseX, int mouseY) {
int p = 10; boolean settingsHover = Utils.isHovering(
int s = 14; mouseX - getSettingsX(),
mouseY - getSettingsY(),
getSettingsHitBox(),
getSettingsHitBox(),
1.0f);
if (Utils.isHovering(mouseX - (width - s - p - 5), mouseY - (height - s - p - 5), s + 10, s + 10, 1.0f)) { if (settingsHover) {
SaturnClient.client.setScreen(new ConfigEditor(mod.getConfig())); SaturnClient.client.setScreen(new ConfigEditor(mod.getConfig()));
} else { } else {
mod.setEnabled(!mod.isEnabled()); mod.setEnabled(!mod.isEnabled());
} }
} }
private int getSettingsX() {
return width - s - p - expand;
}
private int getSettingsY() {
return height - s - p - expand;
}
private int getSettingsHitBox() {
return s + expand * 2;
}
} }
@@ -1,9 +1,8 @@
package org.saturnclient.ui2.components; package org.saturnclient.ui2.components;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
@@ -18,19 +17,6 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class Sidebar extends Element { public class Sidebar extends Element {
private static ThemeManager theme = new ThemeManager("SideBar", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFFFFFFF));
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0x00000000));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
private static Property<Integer> iconRadius = theme.property("icon-radius", Property.integer(10));
private static Property<Integer> padding = theme.property("padding", Property.integer(10));
static {
theme.propertyStateDefault("hovering", "icon-bg", -7643914);
}
public static class SidebarComponent { public static class SidebarComponent {
Runnable onClick; Runnable onClick;
boolean end; boolean end;
@@ -45,7 +31,7 @@ public class Sidebar extends Element {
Runnable onClose; Runnable onClose;
SidebarComponent[] components = { SidebarComponent[] components = {
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> { new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
SaturnClient.client.setScreen(new ModMenu()); SaturnClient.client.setScreen(new ModMenu());
}, false), }, false),
new Sidebar.SidebarComponent(Textures.SETTINGS, () -> { new Sidebar.SidebarComponent(Textures.SETTINGS, () -> {
@@ -79,36 +65,29 @@ public class Sidebar extends Element {
int bottomRow = 0; int bottomRow = 0;
int gap = 5; // 5px gap between icons int gap = 5; // 5px gap between icons
int sp = 30 - padding.value + gap; int padding = 10;
int s = 30 - (padding.value * 2); int sp = 30 - padding + gap;
int s2 = s + padding.value; int s = 30 - (padding * 2);
int s2 = s + padding;
renderScope.setRenderLayer(RenderLayer::getGuiTextured); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value); renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
int i = 0; int i = 0;
for (SidebarComponent component : components) { for (SidebarComponent component : components) {
int y = component.end ? ((height - (sp * bottomRow)) - (s * 2)) : (padding + (sp * topRow));
boolean selected = ctx.isHovering(0, y - (padding / 2), s2, s2) || active == i;
renderScope.drawRoundedRectangle(padding / 2, y - (padding / 2), s + padding,
s + padding, Theme.WIDGET_RADIUS.value, Theme.getBg(selected));
renderScope.drawTexture(component.sprite, padding, y, 0, 0, s, s, Theme.getFg(selected));
if (component.end) { if (component.end) {
int y = (height - (sp * bottomRow)) - (s * 2);
if (ctx.isHovering(0, y - (padding.value / 2), s2, s2) || active == i) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(padding.value / 2, y - (padding.value / 2), s + padding.value, s + padding.value, iconRadius.value, iconBg.value);
renderScope.drawTexture(component.sprite, padding.value, y, 0, 0, s, s, iconFg.value);
bottomRow += 1; bottomRow += 1;
} else { } else {
int y = padding.value + (sp * topRow);
if (ctx.isHovering(0, y - (padding.value / 2), s2, s2) || active == i) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(padding.value / 2, y - (padding.value / 2), s + padding.value, s + padding.value, iconRadius.value, iconBg.value);
renderScope.drawTexture(component.sprite, padding.value, y, 0, 0, s, s, iconFg.value);
topRow += 1; topRow += 1;
} }
@@ -121,25 +100,24 @@ public class Sidebar extends Element {
int topRow = 0; int topRow = 0;
int bottomRow = 0; int bottomRow = 0;
int gap = 5; // same 5px gap here int gap = 5;
int sp = 30 - padding.value + gap; int padding = 10;
int s = 30 - (padding.value * 2); int sp = 30 - padding + gap;
int s2 = s + padding.value; int s = 30 - (padding * 2);
int s2 = s + padding;
for (SidebarComponent component : components) { for (SidebarComponent component : components) {
int y = component.end ? ((height - (sp * bottomRow)) - (s * 2)) : (padding + (sp * topRow));
boolean hovering = Utils.isHovering(mouseX - (padding / 2), mouseY - (y - (padding / 2)), s2, s2, scale);
if (hovering) {
component.onClick.run();
break;
}
if (component.end) { if (component.end) {
int y = (height - (sp * bottomRow)) - (s * 2);
if (Utils.isHovering(mouseX - (padding.value / 2), mouseY - (y - (padding.value / 2)), s2, s2, scale)) {
component.onClick.run();
break;
}
bottomRow += 1; bottomRow += 1;
} else { } else {
int y = padding.value + (sp * topRow);
if (Utils.isHovering(mouseX - (padding.value / 2), mouseY - (y - (padding.value / 2)), s2, s2, scale)) {
component.onClick.run();
break;
}
topRow += 1; topRow += 1;
} }
} }
@@ -1,246 +0,0 @@
package org.saturnclient.ui2.components.dialog;
import java.awt.Color;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import net.minecraft.client.gui.DrawContext;
public class ColorDialog extends SaturnScreen {
private Property<Integer> prop;
private SaturnScreen parent;
private static final int PICKER_WIDTH = 200;
private static final int PICKER_HEIGHT = 200;
private static final int SLIDER_WIDTH = 200;
private static final int SLIDER_HEIGHT = 10;
private static final int HUE_SLIDER_HEIGHT = 10;
private float hue = 0.0f;
private int selectedX = 0;
private int selectedY = 0;
private float opacity = 1.0f;
private boolean dragging = false;
public ColorDialog(Property<Integer> prop , SaturnScreen parent) {
super("Color Dialog");
this.prop = prop;
this.parent = parent;
int alpha = (prop.value >> 24) & 0xFF;
if (alpha == 0) alpha = 255; // fallback if no alpha specified
this.opacity = alpha / 255.0f;
syncFromColor();
}
@Override
public void ui() {}
@Override
public void close() {
SaturnClient.client.setScreen(parent);
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
renderScope.matrices.push();
renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
int xStart = (width - PICKER_WIDTH) / 2;
int yStart = (height - PICKER_HEIGHT) / 2;
// Draw the HSV saturation/value square (with current hue)
for (int x = 0; x < PICKER_WIDTH; x++) {
for (int y = 0; y < PICKER_HEIGHT; y++) {
float saturation = x / (float) PICKER_WIDTH;
float value = 1.0f - y / (float) PICKER_HEIGHT;
int color = Color.HSBtoRGB(hue, saturation, value);
context.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, 0xFF000000 | color);
}
}
int x = (xStart + selectedX) - 4;
int y = (yStart + selectedY) - 4;
context.fill(x, y, x+8, y+8, 0xFF000000 | prop.value);
context.drawBorder(x, y, 8, 8, 0xFFffffff);
renderSlider(context, mouseX, mouseY, delta);
renderHueSlider(context);
renderScope.matrices.pop();
}
public void renderSlider(DrawContext context, int mouseX, int mouseY, float delta) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10;
// Draw slider background (left = transparent, right = white)
for (int x = 0; x < SLIDER_WIDTH; x++) {
float localOpacity = x / (float) SLIDER_WIDTH;
int alpha = (int)(localOpacity * 255) & 0xFF;
int color = (alpha << 24) | (prop.value & 0xFFFFFF);
context.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color);
}
// Draw current value as a marker (thin black line)
int knobX = xStart + (int)(opacity * SLIDER_WIDTH);
context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000);
// Draw opacity text
// context.drawText(textRenderer, String.format("Opacity: %.2f", opacity), xStart, yStart - 15, 0xFFFFFF, false);
}
private void renderHueSlider(DrawContext context) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height + PICKER_HEIGHT) / 2 + 10;
for (int x = 0; x < SLIDER_WIDTH; x++) {
float localHue = x / (float) SLIDER_WIDTH;
int color = Color.HSBtoRGB(localHue, 1.0f, 1.0f);
context.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color);
}
int knobX = xStart + (int)(hue * SLIDER_WIDTH);
context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouseX *= 2;
mouseY *= 2;
int xStart = (width - PICKER_WIDTH) / 2;
int yStart = (height - PICKER_HEIGHT) / 2;
int relX = (int) mouseX - xStart;
int relY = (int) mouseY - yStart;
if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) {
float saturation = relX / (float) PICKER_WIDTH;
float value = 1.0f - relY / (float) PICKER_HEIGHT;
prop.value = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
selectedX = relX;
selectedY = relY;
return true;
} else if (isInSlider(mouseX, mouseY)) {
updateOpacity(mouseX);
dragging = true;
return true;
} else if (isInHueSlider(mouseX, mouseY)) {
updateHue(mouseX);
return true;
}
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
dragging = false;
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
mouseX *= 2;
mouseY *= 2;
if (isInHueSlider(mouseX, mouseY)) {
updateHue(mouseX);
return true;
} else if (dragging) {
updateOpacity(mouseX);
return true;
} else {
int xStart = (width - PICKER_WIDTH) / 2;
int yStart = (height - PICKER_HEIGHT) / 2;
int relX = (int) mouseX - xStart;
int relY = (int) mouseY - yStart;
if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) {
float saturation = relX / (float) PICKER_WIDTH;
float value = 1.0f - relY / (float) PICKER_HEIGHT;
prop.value = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
selectedX = relX;
selectedY = relY;
return true;
}
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
private boolean isInSlider(double mouseX, double mouseY) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10;
return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH &&
mouseY >= yStart && mouseY <= yStart + SLIDER_HEIGHT;
}
private boolean isInHueSlider(double mouseX, double mouseY) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height + PICKER_HEIGHT) / 2 + 10;
return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH &&
mouseY >= yStart && mouseY <= yStart + HUE_SLIDER_HEIGHT;
}
private void updateOpacity(double mouseX) {
int xStart = (width - SLIDER_WIDTH) / 2;
float newOpacity = (float)((mouseX - xStart) / SLIDER_WIDTH);
opacity = Math.max(0.0f, Math.min(1.0f, newOpacity));
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
}
private void updateHue(double mouseX) {
int xStart = (width - SLIDER_WIDTH) / 2;
float newHue = (float)((mouseX - xStart) / SLIDER_WIDTH);
hue = Math.max(0.0f, Math.min(1.0f, newHue));
float saturation = selectedX / (float) PICKER_WIDTH;
float value = 1.0f - selectedY / (float) PICKER_HEIGHT;
prop.value = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
}
private void syncFromColor() {
int rgb = prop.value & 0xFFFFFF;
float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null);
this.hue = hsb[0];
float saturation = hsb[1];
float value = hsb[2];
this.selectedX = (int)(saturation * PICKER_WIDTH);
this.selectedY = (int)((1.0f - value) * PICKER_HEIGHT);
// Extract alpha and convert to opacity float
int alpha = (prop.value >> 24) & 0xFF;
if (alpha == 0) alpha = 255;
this.opacity = alpha / 255.0f;
}
}
@@ -1,32 +1,183 @@
package org.saturnclient.ui2.components.inputs; package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.SaturnClient; import java.awt.Color;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.components.dialog.ColorDialog;
public class HexInput extends Element { public class HexInput extends Element {
public Property<Integer> prop; public Property<Integer> prop;
public SaturnScreen screen;
public HexInput(Property<Integer> prop, SaturnScreen screen) { // Picker state
private boolean open = false;
private float hue = 0f;
private float opacity = 1f;
private int selectedX = 0;
private int selectedY = 0;
private boolean draggingOpacity = false;
// Dimensions
private static final int PICKER_WIDTH = 100;
private static final int PICKER_HEIGHT = 100;
private static final int SLIDER_WIDTH = 100;
private static final int SLIDER_HEIGHT = 10;
private static final int HUE_SLIDER_HEIGHT = 10;
public HexInput(Property<Integer> prop) {
this.prop = prop;
this.width = 58; this.width = 58;
this.height = 20; this.height = 20;
this.prop = prop; syncFromColor();
this.scale = 0.8f;
this.screen = screen;
} }
@Override @Override
public void click(int mouseX, int mouseY) { public void click(int mouseX, int mouseY) {
SaturnClient.client.setScreen(new ColorDialog(prop, screen)); open = !open;
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (open) {
handlePickerClick(mouseX - width - 8, mouseY);
}
}
@Override
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
if (open) {
handlePickerDrag(mouseX - width - 8, mouseY);
}
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
draggingOpacity = false;
} }
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, 17, prop.value); // Draw the main color rectangle
renderScope.drawRoundedRectangle(0, 0, width, height, 6, prop.value);
if (!open)
return;
// Draw the picker relative to (width + 8, 0)
int pickerX = width + 8;
int pickerY = 0;
renderColorSquare(renderScope, pickerX, pickerY);
renderOpacitySlider(renderScope, pickerX, pickerY - SLIDER_HEIGHT - 10);
renderHueSlider(renderScope, pickerX, pickerY + PICKER_HEIGHT + 10);
}
private void renderColorSquare(RenderScope renderScope, int xStart, int yStart) {
for (int x = 0; x < PICKER_WIDTH; x++) {
for (int y = 0; y < PICKER_HEIGHT; y++) {
float saturation = x / (float) PICKER_WIDTH;
float value = 1f - y / (float) PICKER_HEIGHT;
int color = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int) (opacity * 255) & 0xFF;
color = (alpha << 24) | (color & 0xFFFFFF);
renderScope.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, color);
}
}
// Draw selection square
int selX = xStart + selectedX - 4;
int selY = yStart + selectedY - 4;
renderScope.fill(selX, selY, selX + 8, selY + 8, 0xFF000000 | prop.value);
renderScope.drawBorder(selX, selY, 8, 8, 0xFFFFFFFF);
}
private void renderOpacitySlider(RenderScope renderScope, int xStart, int yStart) {
for (int x = 0; x < SLIDER_WIDTH; x++) {
float localOpacity = x / (float) SLIDER_WIDTH;
int alpha = (int) (localOpacity * 255) & 0xFF;
int color = (alpha << 24) | (prop.value & 0xFFFFFF);
renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color);
}
int knobX = xStart + (int) (opacity * SLIDER_WIDTH);
renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000);
}
private void renderHueSlider(RenderScope renderScope, int xStart, int yStart) {
for (int x = 0; x < SLIDER_WIDTH; x++) {
float localHue = x / (float) SLIDER_WIDTH;
int color = Color.HSBtoRGB(localHue, 1f, 1f);
renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color);
}
int knobX = xStart + (int) (hue * SLIDER_WIDTH);
renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000);
}
private void handlePickerClick(double mouseX, double mouseY) {
// Color square
if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) {
selectedX = (int) mouseX;
selectedY = (int) mouseY;
updateColorFromSelection();
}
// Opacity slider
int opY = -SLIDER_HEIGHT - 10;
if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= opY && mouseY <= opY + SLIDER_HEIGHT) {
updateOpacity(mouseX);
draggingOpacity = true;
}
// Hue slider
int hueY = PICKER_HEIGHT + 10;
if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= hueY && mouseY <= hueY + HUE_SLIDER_HEIGHT) {
updateHue(mouseX);
}
}
private void handlePickerDrag(double mouseX, double mouseY) {
// Opacity
if (draggingOpacity)
updateOpacity(mouseX);
// Color square
if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) {
selectedX = (int) mouseX;
selectedY = (int) mouseY;
updateColorFromSelection();
}
}
private void updateColorFromSelection() {
float saturation = selectedX / (float) PICKER_WIDTH;
float value = 1f - selectedY / (float) PICKER_HEIGHT;
int color = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int) (opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (color & 0xFFFFFF);
}
private void updateHue(double mouseX) {
hue = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH));
updateColorFromSelection();
}
private void updateOpacity(double mouseX) {
opacity = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH));
updateColorFromSelection();
}
private void syncFromColor() {
int rgb = prop.value & 0xFFFFFF;
float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null);
hue = hsb[0];
selectedX = (int) (hsb[1] * PICKER_WIDTH);
selectedY = (int) ((1f - hsb[2]) * PICKER_HEIGHT);
int alpha = (prop.value >> 24) & 0xFF;
if (alpha == 0)
alpha = 255;
opacity = alpha / 255f;
} }
} }
@@ -2,21 +2,20 @@ package org.saturnclient.ui2.components.inputs;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
public abstract class Input extends Element { public abstract class Input extends Element {
private static ThemeManager theme = new ThemeManager("Input");
private static Property<Integer> font = theme.property("font", Property.font(1));
String text = ""; String text = "";
public int cursorPosition = 0; public int cursorPosition = 0;
public abstract void checkReset(); public abstract void checkReset();
public abstract void backspace(); public abstract void backspace();
public boolean validateBackspace() { public boolean validateBackspace() {
return true; return true;
} }
@@ -71,11 +70,12 @@ public abstract class Input extends Element {
int scrollOffset = getScrollOffset(); int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset); String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA; int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawText(0.6f, visibleText, 4, 4, font.value, textColor); renderScope.drawText(0.6f, visibleText, 4, 4, Theme.FONT.value, textColor);
if (focused) { if (focused) {
int cursorX = 4 + int cursorX = 4 +
(int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), font.value) * 0.6f); (int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), Theme.FONT.value)
* 0.6f);
renderScope.drawRect(cursorX, 2, 1, height - 4, 0xFFFFFFFF); renderScope.drawRect(cursorX, 2, 1, height - 4, 0xFFFFFFFF);
} }
@@ -90,7 +90,7 @@ public abstract class Input extends Element {
int maxWidth = width - 4; // Available width for text int maxWidth = width - 4; // Available width for text
while (offset < cursorPosition) { // Ensure cursor remains visible while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = text.substring(offset, cursorPosition); String visibleText = text.substring(offset, cursorPosition);
if (Fonts.getWidth(visibleText, font.value) > maxWidth) { if (Fonts.getWidth(visibleText, Theme.FONT.value) > maxWidth) {
offset++; offset++;
} else { } else {
break; break;
@@ -104,7 +104,7 @@ public abstract class Input extends Element {
// Ensure text does not overflow past the available width // Ensure text does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) { for (int i = 1; i <= visibleText.length(); i++) {
if (Fonts.getWidth(visibleText.substring(0, i), font.value) > width - 4) { if (Fonts.getWidth(visibleText.substring(0, i), Theme.FONT.value) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow return visibleText.substring(0, i - 1); // Trim overflow
} }
} }
@@ -2,15 +2,13 @@ package org.saturnclient.ui2.components.inputs;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
public class KeybindingSelector extends Element { public class KeybindingSelector extends Element {
private static ThemeManager theme = new ThemeManager("Input");
private static Property<Integer> font = theme.property("font", Property.font(1));
Property<Integer> prop; Property<Integer> prop;
public KeybindingSelector(Property<Integer> prop) { public KeybindingSelector(Property<Integer> prop) {
@@ -23,9 +21,12 @@ public class KeybindingSelector extends Element {
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
int textColor = focused ? 0xFFFFFF : 0xAAAAAA; int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000); renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
renderScope.drawText(0.6f, prop.value == -1 ? "<NONE>" : String name = GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value));
GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value)).toUpperCase(),
4, 4, font.value, textColor); renderScope.drawText(0.6f,
prop.value == -1 ? "<NONE>"
: name != null ? name.toUpperCase() : "<Invalid-Key>",
4, 4, Theme.FONT.value, textColor);
} }
@Override @Override
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.components.inputs; package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -10,11 +10,6 @@ import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
public class Select extends Element { public class Select extends Element {
private static ThemeManager theme = new ThemeManager("Toggle");
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xAAAAAA));
private static Property<Integer> arrowColor = theme.property("arrow-color", Property.color(0xff8B5CF6));
private static Property<Integer> font = theme.property("font", Property.font(1));
private Property<Integer> prop; private Property<Integer> prop;
public Select(Property<Integer> prop) { public Select(Property<Integer> prop) {
@@ -27,15 +22,14 @@ public class Select extends Element {
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.setRenderLayer(RenderLayer::getGuiTextured); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawText(0.8f, "<", 0, 0, font.value, arrowColor.value); renderScope.drawText(0.8f, "<", 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
String text = prop.getSelection(); String text = prop.getSelection();
renderScope.drawText(0.6f, text, (int) (width - ((float) Fonts.getWidth(text, font.value) * 0.6f)) / 2, 1, font.value, fgColor.value); renderScope.drawText(0.6f, text, (int) (width - ((float) Fonts.getWidth(text, Theme.FONT.value) * 0.6f)) / 2, 1,
Theme.FONT.value, Theme.FOREGROUND.value);
renderScope.drawText(0.8f, ">", width - 10, 0, font.value, arrowColor.value); renderScope.drawText(0.8f, ">", width - 10, 0, Theme.FONT.value, Theme.FOREGROUND.value);
theme.setState(null);
} }
@Override @Override
@@ -1,22 +1,12 @@
package org.saturnclient.ui2.components.inputs; package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
public class Toggle extends Element { public class Toggle extends Element {
private static ThemeManager theme = new ThemeManager("Toggle", "hovering", "enabled");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF808080));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFF909090));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(30));
static {
theme.propertyStateDefault("enabled", "bg-color", 0xaf8B5CF6);
theme.propertyStateDefault("enabled", "fg-color", 0xff8B5CF6);
}
private Property<Boolean> prop; private Property<Boolean> prop;
public Toggle(Property<Boolean> prop) { public Toggle(Property<Boolean> prop) {
@@ -27,19 +17,10 @@ public class Toggle extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
theme.setState(null); renderScope.drawRoundedRectangle(0, 0, width, height, height,
Theme.withAlpha(0.5f, prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value));
if (prop.value) { renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30,
theme.setState("enabled"); prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value);
}
if (ctx.isHovering()) {
theme.applyState("hovering");
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30, fgColor.value);
} }
@Override @Override
@@ -0,0 +1,58 @@
package org.saturnclient.ui2.elements;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.components.ElementRenderer;
public class AnimationStagger extends Element {
protected List<Element> children = new ArrayList<>();
public int delay;
public AnimationStagger(int delay) {
this.delay = delay;
}
public void draw(Element element) {
if (!SaturnClientConfig.stagger.value) {
element.animation = null;
}
if (element.animation != null) {
element.animation.delay = delay * children.size();
}
ElementRenderer.draw(children, element);
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
ElementRenderer.render(children, ctx.elapsed, renderScope,
ctx.mouseX,
ctx.mouseY);
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
ElementRenderer.mouseClicked(children, mouseX, mouseY, button);
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers);
}
@Override
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
ElementRenderer.mouseDragged(children, mouseX, mouseY, button, deltaX, deltaY);
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
ElementRenderer.mouseReleased(children, mouseX, mouseY, button);
}
}
@@ -1,43 +1,29 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
public class Button extends Element { public class Button extends Element {
private static ThemeManager theme = new ThemeManager("Button", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
private static Property<Integer> font = theme.property("font", Property.font(1));
static {
theme.propertyStateDefault("hovering", "fg-color", 0xFF845eee);
}
private String text; private String text;
private Runnable onClick; private Runnable onClick;
public Button(String text, Runnable onClick) { public Button(String text, Runnable onClick) {
this.text = text; this.text = text;
this.onClick = onClick; this.onClick = onClick;
this.dimensions(Fonts.getWidth(text, font.value) + 50, 38); this.dimensions(Fonts.getWidth(text, Theme.FONT.value) + 50, 38);
} }
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) { renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
theme.setState("hovering"); ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
// renderScope.drawText(text, (width - Fonts.getWidth(text, font)) / 2, (height - Fonts.getHeight()) / 2, bold.value, fgColor.value); renderScope.drawText(text, Fonts.centerX(width, text, Theme.FONT.value), Fonts.centerY(height),
renderScope.drawText(text, Fonts.centerX(width, text, font.value), Fonts.centerY(height), font.value, fgColor.value); Theme.FONT.value,
ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
} }
@Override @Override
@@ -1,7 +1,6 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -10,9 +9,6 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class ClickableTexture extends Element { public class ClickableTexture extends Element {
private static ThemeManager theme = new ThemeManager("ClickableTexture", "hovering");
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
Identifier sprite; Identifier sprite;
private Runnable onClick; private Runnable onClick;
@@ -23,14 +19,9 @@ public class ClickableTexture extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.setRenderLayer(RenderLayer::getGuiTextured); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height, fgColor.value); renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
} }
@Override @Override
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -10,11 +10,9 @@ import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
public class Notification extends Element { public class Notification extends Element {
private static ThemeManager theme = new ThemeManager("Notification"); private static Property<Integer> error = Property.color(0xFFbf212f);
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000)); private static Property<Integer> info = Property.color(0xFF264b96);
private static Property<Integer> error = theme.property("error", Property.color(0xFFbf212f)); private static Property<Integer> success = Property.color(0xFF27b376);
private static Property<Integer> info = theme.property("info", Property.color(0xFF264b96));
private static Property<Integer> success = theme.property("success", Property.color(0xFF27b376));
public static enum NotificationKind { public static enum NotificationKind {
Error, Error,
@@ -40,7 +38,7 @@ public class Notification extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, 10, bgColor.value); renderScope.drawRoundedRectangle(0, 0, width, height, 10, Theme.PRIMARY.value);
switch (kind) { switch (kind) {
case Error: case Error:
renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value); renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value);
@@ -4,21 +4,16 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.components.ElementRenderer; import org.saturnclient.ui2.components.ElementRenderer;
public class Scroll extends Element { public class Scroll extends Element {
private static ThemeManager theme = new ThemeManager("Scroll"); private static Property<Integer> scrollBarRadius = Property.integer(10);
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000)); private static Property<Integer> scrollBarWidth = Property.integer(5);
private static Property<Integer> scrollBarColor = theme.property("scrollbar-color", Property.color(-7643914)); private static Property<Integer> scrollBarPadding = Property.integer(5);
private static Property<Integer> scrollBarRadius = theme.property("scrollbar-radius", Property.integer(10));
private static Property<Integer> scrollBarWidth = theme.property("scrollbar-width", Property.integer(5));
private static Property<Integer> scrollBarPadding = theme.property("scrollbar-padding", Property.integer(5));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
int padding = 0; int padding = 0;
@@ -30,18 +25,37 @@ public class Scroll extends Element {
this.padding = padding; this.padding = padding;
} }
public void draw(Element element) {
ElementRenderer.draw(children, element);
}
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value); calculateMaxScroll();
renderScope.drawRoundedRectangle(0, 0, width, height,
Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
renderScope.enableScissor(padding, padding, width - padding, height - padding); renderScope.enableScissor(padding, padding, width - padding, height - padding);
renderScope.matrices.push(); renderScope.matrices.push();
renderScope.matrices.translate(padding, (-scroll) + padding, 0); renderScope.matrices.translate(padding, -scroll + padding, 0);
ElementRenderer.render(children, renderScope, ctx.mouseX - padding, ctx.mouseY - padding + scroll);
ElementRenderer.render(children, ctx.elapsed, renderScope,
ctx.mouseX - padding,
ctx.mouseY - padding + scroll);
renderScope.matrices.pop(); renderScope.matrices.pop();
renderScope.disableScissor(); renderScope.disableScissor();
renderScope.drawRoundedRectangle(width - scrollBarWidth.value - scrollBarPadding.value, calculateScrollBarY(), scrollBarWidth.value, calculateScrollBarHeight(), scrollBarRadius.value, scrollBarColor.value); if (maxScroll > 0) {
renderScope.drawRoundedRectangle(
width - scrollBarWidth.value - scrollBarPadding.value,
calculateScrollBarY(),
scrollBarWidth.value,
calculateScrollBarHeight(),
scrollBarRadius.value,
Theme.SCROLL.value);
}
} }
@Override @Override
@@ -54,36 +68,63 @@ public class Scroll extends Element {
} }
} }
@Override
public void click(int mouseX, int mouseY) {
ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, 0);
}
public void draw(Element element) {
children.add(element);
}
@Override @Override
public Element dimensions(int width, int height) { public Element dimensions(int width, int height) {
maxScroll = 0;
for (Element element : children) { for (Element element : children) {
maxScroll = Math.max(maxScroll, element.y + element.height - height); maxScroll = Math.max(maxScroll, element.y + element.height - height);
} }
maxScroll = Math.max(0, maxScroll); // never negative
return super.dimensions(width, height); return super.dimensions(width, height);
} }
int calculateScrollBarHeight() { int calculateScrollBarHeight() {
if (maxScroll <= 0) return height - (scrollBarPadding.value * 2); // No scrolling needed if (maxScroll <= 0)
return Math.max(20, (height * height) / (height + maxScroll)) - (scrollBarPadding.value * 2); // At least 20px tall return height - (scrollBarPadding.value * 2);
return Math.max(20, (height * height) / (height + maxScroll + 10)) - (scrollBarPadding.value * 2);
} }
int calculateScrollBarY() { int calculateScrollBarY() {
int scrollBarHeight = calculateScrollBarHeight(); int scrollBarHeight = calculateScrollBarHeight();
if (maxScroll <= 0) return scrollBarPadding.value; if (maxScroll <= 0)
return scrollBarPadding.value;
return (scroll * (height - scrollBarHeight)) / maxScroll + scrollBarPadding.value; return (scroll * (height - scrollBarHeight)) / maxScroll + scrollBarPadding.value;
} }
private void calculateMaxScroll() {
maxScroll = 0;
for (Element element : children) {
int bottom = element.y + element.height;
maxScroll = Math.max(maxScroll, bottom - height + padding);
}
if (maxScroll > 0) {
maxScroll = maxScroll + 10;
} else {
maxScroll = 0;
}
}
@Override @Override
public void keyPressed(int keyCode, int scanCode, int modifiers) { public void keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers); ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers);
} }
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, button);
}
@Override
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
ElementRenderer.mouseDragged(children, mouseX - padding, mouseY - padding + scroll, button, deltaX, deltaY);
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
ElementRenderer.mouseReleased(children, mouseX - padding, mouseY - padding + scroll, button);
}
} }
@@ -1,7 +1,6 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -10,16 +9,6 @@ import org.saturnclient.ui2.Utils;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class TabMenu extends Element { public class TabMenu extends Element {
private static ThemeManager theme = new ThemeManager("TabMenu", "hovering", "selected");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFFFFFFF));
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0x00000000));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
static {
theme.propertyStateDefault("hovering", "icon-bg", -7643914);
}
public static class TabMenuComponent { public static class TabMenuComponent {
Runnable onClick; Runnable onClick;
Identifier sprite; Identifier sprite;
@@ -42,19 +31,21 @@ public class TabMenu extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value); renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Theme.PRIMARY.value);
int col = 0; int col = 0;
for (TabMenuComponent component : components) { for (TabMenuComponent component : components) {
int x = 25 * col + 5; int x = 25 * col + 5;
if (ctx.isHovering(x, 5, 20, 20) || selectedScreen == col) {
theme.setState("hovering"); boolean hovering = ctx.isHovering(x, 5, 20, 20) || selectedScreen == col;
if (hovering) {
renderScope.drawRoundedRectangle(x, 5, 20, 20, Theme.WIDGET_RADIUS.value, Theme.ACCENT.value);
} }
renderScope.drawRoundedRectangle(x, 5, 20, 20, cornerRadius.value, iconBg.value); renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10,
renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10, iconFg.value); hovering ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
theme.setState(null);
col++; col++;
} }
} }
@@ -1,27 +1,22 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
public class Text extends Element { public class Text extends Element {
private static ThemeManager theme = new ThemeManager("Text");
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
public static Property<Integer> font = theme.property("font", Property.font(1));
private String text; private String text;
public Text(String text) { public Text(String text) {
this.text = text; this.text = text;
width = Fonts.getWidth(text, font.value); width = Fonts.getWidth(text, Theme.FONT.value);
height = Fonts.getHeight(); height = Fonts.getHeight();
} }
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawText(this.text, 0, 0, font.value, fgColor.value); renderScope.drawText(this.text, 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
} }
} }
@@ -1,7 +1,6 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -10,19 +9,9 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class TextureButton extends Element { public class TextureButton extends Element {
private static ThemeManager theme = new ThemeManager("TextureButton", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
private static Property<Integer> padding = theme.property("padding", Property.integer(14));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
static {
theme.propertyStateDefault("hovering", "fg-color", 0xFF845eee);
}
Identifier sprite; Identifier sprite;
private Runnable onClick; private Runnable onClick;
static int padding = 14;
public TextureButton(Identifier sprite, Runnable onClick) { public TextureButton(Identifier sprite, Runnable onClick) {
this.sprite = sprite; this.sprite = sprite;
@@ -31,20 +20,16 @@ public class TextureButton extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) { renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
theme.setState("hovering"); ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding.value; int texWidth = width - padding;
int texHeight = height - padding.value; int texHeight = height - padding;
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value); renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
} }
@Override @Override
@@ -4,6 +4,7 @@ import java.util.Map;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.components.Sidebar; import org.saturnclient.ui2.components.Sidebar;
import org.saturnclient.ui2.components.inputs.FloatInput; import org.saturnclient.ui2.components.inputs.FloatInput;
@@ -59,14 +60,13 @@ public class ConfigEditor extends SaturnScreen {
if (prop.getType() == Property.PropertyType.NAMESPACE) { if (prop.getType() == Property.PropertyType.NAMESPACE) {
configScroll.draw( configScroll.draw(
new Text( new Text(
propName propName).position(Fonts.centerX(520, propName, Theme.FONT.value), modY + 1)
).position(Fonts.centerX(520, propName, Text.font.value), modY + 1).scale(0.7f)); .scale(0.7f));
} else { } else {
configScroll.draw( configScroll.draw(
new Text( new Text(
propName propName).position(modX, modY + 1).scale(0.7f));
).position(modX, modY + 1).scale(0.7f));
} }
switch (prop.getType()) { switch (prop.getType()) {
@@ -78,7 +78,7 @@ public class ConfigEditor extends SaturnScreen {
break; break;
case HEX: case HEX:
configScroll.draw(new HexInput((Property<Integer>) prop, this).position(w / 2, modY)); configScroll.draw(new HexInput((Property<Integer>) prop).position(w / 2, modY));
break; break;
case INTEGER: case INTEGER:
@@ -1,8 +1,8 @@
package org.saturnclient.ui2.screens; package org.saturnclient.ui2.screens;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.client.ServiceClient; import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.cosmetics.Emotes; import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.Utils; import org.saturnclient.ui2.Utils;
@@ -3,8 +3,10 @@ package org.saturnclient.ui2.screens;
import org.saturnclient.modules.ModManager; import org.saturnclient.modules.ModManager;
import org.saturnclient.modules.Module; import org.saturnclient.modules.Module;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.anim.SlideFade;
import org.saturnclient.ui2.components.SaturnModule; import org.saturnclient.ui2.components.SaturnModule;
import org.saturnclient.ui2.components.Sidebar; import org.saturnclient.ui2.components.Sidebar;
import org.saturnclient.ui2.elements.AnimationStagger;
import org.saturnclient.ui2.elements.ImageTexture; import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.elements.Scroll; import org.saturnclient.ui2.elements.Scroll;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
@@ -24,8 +26,11 @@ public class ModMenu extends SaturnScreen {
int row = 0; int row = 0;
int col = 0; int col = 0;
AnimationStagger moduleStagger = new AnimationStagger(80);
for (Module mod : ModManager.ALL_MODS) { for (Module mod : ModManager.ALL_MODS) {
scroll.draw(new SaturnModule(mod).position((160 + g) * col, (50 + g) * row)); moduleStagger.draw(
new SaturnModule(mod).position((160 + g) * col, (50 + g) * row).animation(new SlideFade(600, -12)));
if (col == 2) { if (col == 2) {
col = 0; col = 0;
@@ -35,6 +40,8 @@ public class ModMenu extends SaturnScreen {
} }
} }
scroll.draw(moduleStagger);
int scrollWidth = 480 + 10 + (g * 2) + (p * 2); int scrollWidth = 480 + 10 + (g * 2) + (p * 2);
draw(scroll.dimensions(scrollWidth, 350).center(width, height)); draw(scroll.dimensions(scrollWidth, 350).center(width, height));
@@ -1,12 +1,12 @@
package org.saturnclient.ui2.screens; package org.saturnclient.ui2.screens;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.menus.HudEditor; import org.saturnclient.saturnclient.menus.HudEditor;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.screens.cosmetics.CloakMenu; import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.elements.AnimationStagger;
import org.saturnclient.ui2.elements.Button; import org.saturnclient.ui2.elements.Button;
import org.saturnclient.ui2.elements.ImageTexture; import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.elements.TextureButton; import org.saturnclient.ui2.elements.TextureButton;
@@ -20,21 +20,42 @@ public class ShiftMenu extends SaturnScreen {
@Override @Override
public void ui() { public void ui() {
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -36).animation(new Fade(700))); // Logo animations (unchanged)
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -80).animation(new SlideY(700, -20))); draw(new ImageTexture(Textures.LOGO_TEXT)
.dimensions(98, 10)
.centerOffset(width, height, 0, -36)
.animation(new Fade(700)));
Element button = new Button("Settings", () -> { draw(new ImageTexture(SaturnClientConfig.getLogo())
SaturnClient.client.setScreen(new ModMenu()); .dimensions(98, 98)
}).center(width, height); .centerOffset(width, height, 0, -80)
.animation(new SlideY(700, -20)));
draw(button); // Spacing and sizes
int btnWidth = 120;
int btnHeight = 40;
int spacing = 4;
draw(new TextureButton(Textures.COSMETICS, () -> { // Create stagger
client.setScreen(new CloakMenu()); AnimationStagger stagger = new AnimationStagger(120);
}).dimensions(button.height, button.height).centerOffset(width, height, (button.width / 2) + (button.height / 2) + 4, 0));
draw(new TextureButton(Textures.HUD_ICON, () -> { stagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
client.setScreen(new HudEditor()); client.setScreen(new HudEditor());
}).dimensions(button.height, button.height).centerOffset(width, height, -((button.width / 2) + (button.height / 2) + 4), 0)); }).dimensions(btnHeight, btnHeight).position(0, 0)
.animation(new Fade(300)));
stagger.draw(new Button("Settings", () -> {
SaturnClient.client.setScreen(new ModMenu());
}).dimensions(btnWidth, btnHeight).position(btnHeight + spacing, 0)
.animation(new Fade(300)));
stagger.draw(new TextureButton(Textures.COSMETICS, () -> {
client.setScreen(new CloakMenu());
}).dimensions(btnHeight, btnHeight).position(btnWidth + btnHeight + (spacing * 2), 0)
.animation(new Fade(300)));
// Draw the stagger at the absolute center of the screen
draw(stagger.dimensions(btnWidth + (btnHeight * 2) + (spacing * 2), btnHeight).centerOffset(width, height, 0,
0));
} }
} }
@@ -1,13 +1,15 @@
package org.saturnclient.ui2.screens; package org.saturnclient.ui2.screens;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig; import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.menus.HudEditor; import org.saturnclient.saturnclient.menus.HudEditor;
import org.saturnclient.ui2.resources.Textures; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.screens.cosmetics.CloakMenu; import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.anim.Fade; import org.saturnclient.ui2.anim.Fade;
import org.saturnclient.ui2.anim.SlideFade;
import org.saturnclient.ui2.anim.SlideY; import org.saturnclient.ui2.anim.SlideY;
import org.saturnclient.ui2.elements.AnimationStagger;
import org.saturnclient.ui2.elements.Button; import org.saturnclient.ui2.elements.Button;
import org.saturnclient.ui2.elements.ImageTexture; import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.elements.TextureButton; import org.saturnclient.ui2.elements.TextureButton;
@@ -19,7 +21,7 @@ import net.minecraft.client.gui.screen.world.SelectWorldScreen;
public class TitleMenu extends SaturnScreen { public class TitleMenu extends SaturnScreen {
public TitleMenu() { public TitleMenu() {
super("Tile Menu"); super("Title Menu");
} }
@Override @Override
@@ -31,34 +33,42 @@ public class TitleMenu extends SaturnScreen {
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -82) draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -82)
.animation(new SlideY(700, -20))); .animation(new SlideY(700, -20)));
draw(new Button("SINGLEPLAYER", () -> { AnimationStagger mainButtonStagger = new AnimationStagger(120);
mainButtonStagger.draw(new Button("SINGLEPLAYER", () -> {
client.setScreen(new SelectWorldScreen(new TitleMenu())); client.setScreen(new SelectWorldScreen(new TitleMenu()));
}).scale(0.69f).dimensions(420, 52).centerOffset(width, height, 0, 0)); }).scale(0.69f).dimensions(420, 52).position(0, 0).animation(new SlideFade(1000, -10)));
draw(new Button("MULTIPLAYER", () -> { mainButtonStagger.draw(new Button("MULTIPLAYER", () -> {
client.setScreen(new MultiplayerScreen(new TitleMenu())); client.setScreen(new MultiplayerScreen(new TitleMenu()));
}).scale(0.69f).dimensions(420, 52).centerOffset(width, height, 0, 40)); }).scale(0.69f).dimensions(420, 52).position(0, 40).animation(new SlideFade(1000, -10)));
draw(new Button("OPTIONS", () -> { mainButtonStagger.draw(new Button("OPTIONS", () -> {
client.setScreen(new OptionsScreen(new TitleMenu(), SaturnClient.client.options)); client.setScreen(new OptionsScreen(new TitleMenu(), SaturnClient.client.options));
}).scale(0.69f).dimensions(206, 52).centerOffset(width, height, -73, 80)); }).scale(0.69f).dimensions(206, 52).position(0, 80).animation(new SlideFade(1000, -10)));
draw(new Button("QUIT", () -> { mainButtonStagger.draw(new Button("QUIT", () -> {
client.scheduleStop(); client.scheduleStop();
}).scale(0.69f).dimensions(206, 52).centerOffset(width, height, 73, 80)); }).scale(0.69f).dimensions(206, 52).position(146, 80).animation(new SlideFade(1000, -10)));
draw(mainButtonStagger.dimensions(289, 143).centerOffset(width, height, 0, 60));
int s = Fonts.getHeight() + 20; int s = Fonts.getHeight() + 20;
draw(new TextureButton(Textures.HUD_ICON, () -> { AnimationStagger saturnStagger = new AnimationStagger(180);
saturnStagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
client.setScreen(new HudEditor()); client.setScreen(new HudEditor());
}).dimensions(s, s).centerHorizontal(width, height, -(s + 2), 8).animation(new SlideY(700, s + 8))); }).dimensions(s, s).position(0, 0).animation(new SlideFade(1000, s + 8)));
draw(new TextureButton(Textures.SETTINGS, () -> { saturnStagger.draw(new TextureButton(Textures.SETTINGS, () -> {
SaturnClient.client.setScreen(new ModMenu()); SaturnClient.client.setScreen(new ModMenu());
}).dimensions(s, s).centerHorizontal(width, height, 0, 8).animation(new SlideY(700, s + 8))); }).dimensions(s, s).position(s + 2, 0).animation(new SlideFade(1000, s + 8)));
draw(new TextureButton(Textures.CLOAK, () -> { saturnStagger.draw(new TextureButton(Textures.CLOAK, () -> {
client.setScreen(new CloakMenu()); client.setScreen(new CloakMenu());
}).dimensions(s, s).centerHorizontal(width, height, s + 2, 8).animation(new SlideY(700, s + 8))); }).dimensions(s, s).position((s + 2) * 2, 0).animation(new SlideFade(1000, s + 8)));
draw(saturnStagger.dimensions(((s + 2) * 2) + s, s).centerHorizontal(width, height, 0, 8));
} }
} }
@@ -3,6 +3,7 @@ package org.saturnclient.ui2.screens.store;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.client.ServiceClient; import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.client.player.SaturnPlayer; import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.Utils; import org.saturnclient.ui2.Utils;
@@ -75,10 +76,10 @@ public class CloakStore extends SaturnScreen {
String t = "100"; String t = "100";
scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Text.font.value), y + 113).scale(0.5f)); scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Theme.FONT.value), y + 113).scale(0.5f));
scroll.draw(new ImageTexture(Textures.COINS).dimensions(16, 16) scroll.draw(new ImageTexture(Textures.COINS).dimensions(16, 16)
.position(x + Fonts.getWidth(t, Text.font.value) - 1, y + 112).scale(0.5f)); .position(x + Fonts.getWidth(t, Theme.FONT.value) - 1, y + 112).scale(0.5f));
if (col == 8) { if (col == 8) {
col = 0; col = 0;
@@ -3,6 +3,7 @@ package org.saturnclient.ui2.screens.store;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.client.ServiceClient; import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.client.player.SaturnPlayer; import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.cosmetics.Hats; import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.ui2.SaturnScreen; import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.Utils; import org.saturnclient.ui2.Utils;
@@ -75,10 +76,10 @@ public class HatStore extends SaturnScreen {
String t = "50"; String t = "50";
scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Text.font.value), y + 53).scale(0.5f)); scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Theme.FONT.value), y + 53).scale(0.5f));
scroll.draw(new ImageTexture(Textures.COINS).dimensions(16, 16) scroll.draw(new ImageTexture(Textures.COINS).dimensions(16, 16)
.position(x + Fonts.getWidth(t, Text.font.value) + 4, y + 52).scale(0.5f)); .position(x + Fonts.getWidth(t, Theme.FONT.value) + 4, y + 52).scale(0.5f));
if (col == 8) { if (col == 8) {
col = 0; col = 0;