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 org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.saturnclient.config.Property.NamedProperty;
import org.saturnclient.saturnclient.config.Property;
public class ModDimensions {
@@ -20,17 +20,17 @@ public class ModDimensions {
public NamedProperty<Map<String, Property<?>>> prop() {
return Property.namespace(Map.of(
"X", x,
"Y", y,
"Scale", scale,
"Background Color", bgColor,
"Foreground Color", fgColor,
"Corner Radius", radius,
"Font", font
)).named("In-Game Display");
"X", x,
"Y", y,
"Scale", scale,
"Background Color", bgColor,
"Foreground Color", fgColor,
"Corner Radius", radius,
"Font", font)).named("In-Game Display");
}
public ModDimensions() {}
public ModDimensions() {
}
public ModDimensions(int width, int height) {
this.width = width;
@@ -1,9 +1,9 @@
package org.saturnclient.modules;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.saturnclient.config.Property.NamedProperty;
import net.minecraft.util.Identifier;
@@ -19,8 +19,11 @@ public abstract class Module {
}
}
public void render(RenderScope scope) {}
public void tick() {}
public void render(RenderScope scope) {
}
public void tick() {
}
public abstract boolean isEnabled();
@@ -9,6 +9,7 @@ import net.minecraft.client.util.math.MatrixStack;
import org.saturnclient.modules.ModManager;
import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
@@ -40,6 +41,10 @@ public class SaturnClient implements ModInitializer {
SaturnClientConfig.init();
ModManager.init();
client.execute(() -> {
SaturnScreen.preload(client);
});
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
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.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.client.ServiceClient;
public class SaturnPlayer {
@@ -22,14 +23,17 @@ public class SaturnPlayer {
this.name = name;
}
@Nullable
public static SaturnPlayer get() {
return PLAYERS.get(ServiceClient.uuid);
}
@Nullable
public static SaturnPlayer get(UUID uuid) {
return PLAYERS.get(uuid);
}
@Nullable
public static SaturnPlayer get(String name) {
UUID uuid = PLAYER_NAMES.get(name);
if (uuid == null)
@@ -1,107 +1,94 @@
package org.saturnclient.saturnclient.config;
import com.google.gson.*;
import org.saturnclient.saturnclient.SaturnClient;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import org.saturnclient.saturnclient.SaturnClient;
import java.util.LinkedHashMap;
import java.util.Map;
public class ConfigManager {
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 String namespace;
private final String[] path;
/**
* Creates a root namespace
*/
public ConfigManager(String namespace) {
this.namespace = namespace;
this.currentMap = new LinkedHashMap<>();
properties.put(namespace, currentMap);
System.out.println("Created namespace: " + namespace);
this.path = new String[] { namespace }; // Path is just ["namespace"]
ROOT.put(namespace, Property.namespace(currentMap));
}
/**
* Creates a sub-namespace inside a parent
*/
public ConfigManager(ConfigManager parent, String namespace) {
this.namespace = namespace;
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) {
property(name, Property.namespace(new LinkedHashMap<>()));
// Build new path: parent path + current namespace
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) {
currentMap.put(name, value);
loadProp(name, value);
loadSingleProperty(name, value);
return value;
}
private <T> void loadProp(String name, Property<T> prop) {
JsonObject source = cachedThemeJson != null ? cachedThemeJson : loadAndCache();
if (source == null)
return;
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;
}
/**
* Add empty namespace
*/
public ConfigManager sub(String name) {
return new ConfigManager(this, name);
}
/**
* Load entire config
*/
public static void load() {
SaturnClient.LOGGER.info("Loading config...");
JsonObject json = loadAndCache();
if (json == null)
return;
properties.forEach((namespace, map) -> {
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);
});
loadRecursive(json, ROOT);
}
/**
* Save entire config
*/
public static void save() {
SaturnClient.LOGGER.info("Saving config...");
JsonObject root = new JsonObject();
properties.forEach((namespace, map) -> {
JsonObject nsJson = new JsonObject();
saveProperties(nsJson, map);
root.add(namespace, nsJson);
});
JsonObject rootJson = new JsonObject();
saveRecursive(rootJson, ROOT);
try {
String formatted = new GsonBuilder().setPrettyPrinting().create().toJson(root);
String formatted = new GsonBuilder()
.setPrettyPrinting()
.create()
.toJson(rootJson);
Files.writeString(configFile.toPath(), formatted);
SaturnClient.LOGGER.info("Config saved successfully.");
} 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() {
@@ -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;
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 {
BOOLEAN,
@@ -277,4 +286,13 @@ public class Property<T> {
if (value != null)
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;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
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,
PlayerEntityRenderState playerEntityRenderState, float f, float g) {
if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible) {
if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible
|| playerEntityRenderState.skinTextures.capeTexture() != null) {
return;
}
if (playerEntityRenderState.skinTextures.capeTexture() != null) {
SaturnPlayer player = SaturnPlayer.get(playerEntityRenderState.name);
if (player == null) {
return;
}
SaturnPlayer player = SaturnPlayer.get(playerEntityRenderState.name);
Identifier customCape = Cloaks.getCurrentCloakTexture(player.uuid);
if (customCape == null || this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack,
@@ -6,8 +6,8 @@ import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.ui2.screens.EmoteWheel;
import org.saturnclient.ui2.screens.ShiftMenu;
@@ -14,7 +14,7 @@ import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
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 java.util.UUID;
@@ -43,8 +43,8 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
UUID uuid = isSaturn(state);
if (uuid != null) {
MutableText iconText = Text.literal(SaturnClientConfig.getSaturnIndicator())
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid)));
MutableText iconText = Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)));
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) {
String name = ((PlayerEntityRenderState) state).name;
SaturnPlayer player = SaturnPlayer.get(name);
return player.uuid;
if (player != null) {
return player.uuid;
}
}
return null;
@@ -1,6 +1,6 @@
package org.saturnclient.saturnclient.mixin;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.ui2.screens.TitleMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -3,6 +3,7 @@ package org.saturnclient.saturnclient.mixin;
import java.util.Optional;
import java.util.function.Consumer;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Textures;
import org.spongepowered.asm.mixin.Mixin;
@@ -40,11 +41,8 @@ public abstract class SplashOverlayMixin {
@Shadow(remap = false)
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
*/
@Overwrite
@@ -80,19 +78,17 @@ public abstract class SplashOverlayMixin {
if (fadeOutProgress >= 1.0F) {
renderCurrentScreen(context, mouseX, mouseY, delta);
int overlayAlpha = computeOverlayAlpha(fadeOutProgress - 1f);
// context.fill(0, 0, screenWidth, screenHeight,
// withAlpha(0x53389E, overlayAlpha));
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
withAlpha(0xFFFFFF, overlayAlpha));
Theme.withAlpha(overlayAlpha, 0xFFFFFFFF));
} else if (reloading) {
if (reloadProgress < 1.0F) {
renderCurrentScreen(context, mouseX, mouseY, delta);
}
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
withAlpha(0xFFFFFF, alpha));
Theme.withAlpha(alpha, 0xFFFFFFFF));
} else {
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
withAlpha(0xFFFFFF, alpha));
Theme.withAlpha(alpha, 0xFFFFFFFF));
}
float u = this.reload.getProgress();
@@ -120,18 +116,19 @@ public abstract class SplashOverlayMixin {
/** Draw the main logo centered */
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,
withAlpha(0xFFFFFF, alpha));
Theme.withAlpha(alpha, 0xFFFFFFFF));
}
private void renderProgressBar(RenderScope scope, DrawContext context, int screenWidth, int screenHeight,
int alpha) {
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 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 */
@@ -7,7 +7,7 @@ import net.minecraft.util.Formatting;
import java.util.UUID;
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.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@@ -34,8 +34,8 @@ public abstract class TabListMixin {
UUID uuid = profile.getId();
if (SaturnPlayer.get(uuid) != null) {
return Text.literal(SaturnClientConfig.getSaturnIndicator())
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid)))
return Text.literal(Roles.getSaturnIndicator())
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
}
return Text.literal(name);
@@ -32,6 +32,22 @@ public class Element {
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) {
this.width = width;
this.height = height;
@@ -4,13 +4,15 @@ public class ElementContext {
public int mouseX, mouseY;
public int elementWidth, elementHeight;
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.mouseY = mouseY - e.y;
this.elementWidth = e.width;
this.elementHeight = e.height;
this.elementScale = e.scale;
this.elapsed = elapsed;
}
public boolean isHovering() {
@@ -398,4 +398,45 @@ public class RenderScope {
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;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
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 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.screen.Screen;
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.text.Text;
import net.minecraft.util.Identifier;
@@ -23,12 +25,12 @@ public abstract class SaturnScreen extends Screen {
private final Pool pool = new Pool(3);
protected List<Element> elements = new ArrayList<>();
protected float backgroundOpacity = 1.0f;
public int blurDuration = 700;
public float blurProgress = 0.0f;
protected Instant start = null;
public int backgroundBlur = 10;
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(
Identifier.of("saturnclient", "textures/gui/title/background/panorama"));
protected static final Identifier PANORAMA = Identifier.of("saturnclient",
"textures/gui/title/background/panorama");
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(PANORAMA);
public static final RotatingCubeMapRenderer ROTATING_PANORAMA_RENDERER;
@@ -36,41 +38,24 @@ public abstract class SaturnScreen extends Screen {
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) {
super(Text.literal(title));
}
public void draw(Element element) {
synchronized (elements) {
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();
}
ElementRenderer.draw(elements, element);
}
@Override
@@ -79,10 +64,6 @@ public abstract class SaturnScreen extends Screen {
width *= 2;
height *= 2;
ui(); // abstraction to render the saturn ui and also render extra stuff here
Animation.execute((Float progress) -> {
blurProgress = progress;
}, blurDuration);
}
public abstract void ui();
@@ -90,6 +71,11 @@ public abstract class SaturnScreen extends Screen {
@SuppressWarnings("deprecation")
@Override
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;
mouseY *= 2;
@@ -100,8 +86,9 @@ public abstract class SaturnScreen extends Screen {
PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
Identifier.ofVanilla("blur"),
DefaultFramebufferSet.MAIN_ONLY);
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);
}
@@ -114,7 +101,7 @@ public abstract class SaturnScreen extends Screen {
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();
}
@@ -129,6 +116,26 @@ public abstract class SaturnScreen extends Screen {
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
public boolean mouseScrolled(
double mouseX,
@@ -1,75 +1,16 @@
package org.saturnclient.ui2.anim;
import java.util.function.Consumer;
import java.util.function.Function;
import org.saturnclient.ui2.Element;
public abstract class Animation {
public int duration;
public int delay = 0;
Animation(int 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 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
public void tick(float progress, Element element) {
element.opacity = targetOpacity * progress;
public void tick(double progress, Element element) {
element.opacity = Math.max(0.1f, targetOpacity * (float) progress);
}
@Override
public void init(Element element) {
targetOpacity = element.opacity;
element.opacity = 0;
element.opacity = 0.1f;
}
}
@@ -10,8 +10,8 @@ public class FadeOut extends Animation {
}
@Override
public void tick(float progress, Element element) {
element.opacity = targetOpacity - progress;
public void tick(double progress, Element element) {
element.opacity = targetOpacity - (float) progress;
}
@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
public void tick(float progress, Element element) {
public void tick(double progress, Element element) {
element.x = elementX + (int) (offset * progress);
}
@@ -24,7 +24,7 @@ public class SlideXAbsolute extends Animation {
}
@Override
public void tick(float progress, Element element) {
public void tick(double progress, Element element) {
element.x = elementX + (int) (offset * progress);
}
@@ -5,7 +5,7 @@ import org.saturnclient.ui2.Element;
public class SlideY extends Animation {
int offset = 20;
int maxOffset = 20;
int elementY;
double startY;
public SlideY(int duration) {
super(duration);
@@ -26,13 +26,14 @@ public class SlideY extends Animation {
}
@Override
public void tick(float progress, Element element) {
element.y = elementY + (int) (offset * progress);
public void init(Element element) {
startY = element.y - offset;
element.y = (int) startY;
}
@Override
public void init(Element element) {
element.y -= offset;
elementY = element.y;
public void tick(double progress, Element element) {
double currentY = startY + offset * progress;
element.y = (int) currentY;
}
}
@@ -1,7 +1,6 @@
package org.saturnclient.ui2.components;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
@@ -10,13 +9,7 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
public class CosmeticPreview extends Element {
private static ThemeManager theme = new ThemeManager("CosmeticPreview", "selected", "hovering");
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 {}
static int padding = 14;
Identifier sprite;
private Runnable onClick;
@@ -33,22 +26,16 @@ public class CosmeticPreview extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else if (isSelected) {
theme.setState("selected");
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
isSelected ? Theme.ACCENT.value : Theme.PRIMARY.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding.value;
int texHeight = height - padding.value;
int texWidth = width - padding;
int texHeight = height - padding;
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value);
theme.setState(null);
renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
Theme.FOREGROUND.value);
}
@Override
@@ -7,7 +7,6 @@ import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.anim.Animation;
public class ElementRenderer {
public static void draw(List<Element> elements, Element element) {
@@ -17,20 +16,18 @@ public class ElementRenderer {
if (element.animation != null) {
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) {
element.playAnimationFrame(elapsed);
renderScope.matrices.push();
renderScope.setOpacity(element.opacity);
renderScope.matrices.translate(element.x, element.y, 0);
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.setRenderLayer(null);
}
@@ -44,7 +41,10 @@ public class ElementRenderer {
int adjustedMouseX = (int) mouseX - element.x;
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.click((int) (adjustedMouseX * element.scale), (int) (adjustedMouseY * element.scale));
}
@@ -58,16 +58,17 @@ public class ElementRenderer {
double mouseY,
double horizontalAmount,
double verticalAmount) {
for (Element element : new ArrayList<>(elements)) {
if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height, element.scale)) {
element.scroll(
(int) mouseX - element.x,
(int) mouseY - element.y,
horizontalAmount,
verticalAmount);
}
}
for (Element element : new ArrayList<>(elements)) {
if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height,
element.scale)) {
element.scroll(
(int) mouseX - element.x,
(int) mouseY - element.y,
horizontalAmount,
verticalAmount);
}
}
}
public static void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) {
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.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element;
@@ -15,23 +14,12 @@ import org.saturnclient.ui2.screens.ConfigEditor;
import net.minecraft.client.render.RenderLayer;
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 {
theme.propertyStateDefault("enabled", "bg", 0xFF1d202d);
theme.propertyStateDefault("enabled", "icon-bg", 0xFF845eee);
theme.propertyStateDefault("hovering", "fg", 0xFF845eee);
}
private final Module mod;
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) {
this.mod = mod;
@@ -41,60 +29,137 @@ public class SaturnModule extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
int p = 10;
int s = 14;
boolean settingsHover = false;
boolean hovered = ctx.isHovering();
boolean enabled = mod.isEnabled();
int iconX = width - s - p;
int iconY = height - s - p;
boolean settingsHover = ctx.isHovering(
getSettingsX(),
getSettingsY(),
getSettingsHitBox(),
getSettingsHitBox());
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
if (mod.isEnabled()) {
theme.setState("enabled");
}
// Resolve theme colors
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)) {
settingsHover = true;
} else if (ctx.isHovering()) {
theme.applyState("hovering");
}
int iconBg = enabled ? Theme.ACCENT.value : Theme.PRIMARY.value;
int iconFg = enabled ? Theme.ACCENT_FG.value : fgColor;
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;
for (String tag : mod.getTags()) {
int xtb = (int) (Fonts.getWidth(tag, 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);
xt += xtb + 9;
int tagWidth = (int) (Fonts.getWidth(tag, Theme.FONT.value) * 0.4f);
renderScope.drawRoundedRectangle(
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) {
theme.setState("hovering");
}
renderScope.drawTexture(Textures.SETTINGS, width - s - p, height - s - p, 0, 0, s, s, fgColor.value);
theme.setState(null);
// Settings icon
renderScope.drawTexture(
Textures.SETTINGS,
iconX,
iconY,
0, 0,
s, s,
settingsColor);
}
@Override
public void click(int mouseX, int mouseY) {
int p = 10;
int s = 14;
boolean settingsHover = Utils.isHovering(
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()));
} else {
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;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
@@ -18,19 +17,6 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
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 {
Runnable onClick;
boolean end;
@@ -45,7 +31,7 @@ public class Sidebar extends Element {
Runnable onClose;
SidebarComponent[] components = {
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
SaturnClient.client.setScreen(new ModMenu());
}, false),
new Sidebar.SidebarComponent(Textures.SETTINGS, () -> {
@@ -79,36 +65,29 @@ public class Sidebar extends Element {
int bottomRow = 0;
int gap = 5; // 5px gap between icons
int sp = 30 - padding.value + gap;
int s = 30 - (padding.value * 2);
int s2 = s + padding.value;
int padding = 10;
int sp = 30 - padding + gap;
int s = 30 - (padding * 2);
int s2 = s + padding;
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;
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) {
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;
} 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;
}
@@ -121,25 +100,24 @@ public class Sidebar extends Element {
int topRow = 0;
int bottomRow = 0;
int gap = 5; // same 5px gap here
int sp = 30 - padding.value + gap;
int s = 30 - (padding.value * 2);
int s2 = s + padding.value;
int gap = 5;
int padding = 10;
int sp = 30 - padding + gap;
int s = 30 - (padding * 2);
int s2 = s + padding;
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) {
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;
} 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;
}
}
@@ -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;
import org.saturnclient.saturnclient.SaturnClient;
import java.awt.Color;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.components.dialog.ColorDialog;
public class HexInput extends Element {
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.height = 20;
this.prop = prop;
this.scale = 0.8f;
this.screen = screen;
syncFromColor();
}
@Override
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
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.saturnclient.saturnclient.SaturnClient;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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 = "";
public int cursorPosition = 0;
public abstract void checkReset();
public abstract void backspace();
public boolean validateBackspace() {
return true;
}
@@ -71,11 +70,12 @@ public abstract class Input extends Element {
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
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) {
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);
}
@@ -90,7 +90,7 @@ public abstract class Input extends Element {
int maxWidth = width - 4; // Available width for text
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = text.substring(offset, cursorPosition);
if (Fonts.getWidth(visibleText, font.value) > maxWidth) {
if (Fonts.getWidth(visibleText, Theme.FONT.value) > maxWidth) {
offset++;
} else {
break;
@@ -104,7 +104,7 @@ public abstract class Input extends Element {
// Ensure text does not overflow past the available width
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
}
}
@@ -2,15 +2,13 @@ package org.saturnclient.ui2.components.inputs;
import org.lwjgl.glfw.GLFW;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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;
public KeybindingSelector(Property<Integer> prop) {
@@ -23,9 +21,12 @@ public class KeybindingSelector extends Element {
public void render(RenderScope renderScope, ElementContext ctx) {
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
renderScope.drawText(0.6f, prop.value == -1 ? "<NONE>" :
GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value)).toUpperCase(),
4, 4, font.value, textColor);
String name = GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value));
renderScope.drawText(0.6f,
prop.value == -1 ? "<NONE>"
: name != null ? name.toUpperCase() : "<Invalid-Key>",
4, 4, Theme.FONT.value, textColor);
}
@Override
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.components.inputs;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
@@ -10,11 +10,6 @@ import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.client.render.RenderLayer;
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;
public Select(Property<Integer> prop) {
@@ -27,15 +22,14 @@ public class Select extends Element {
public void render(RenderScope renderScope, ElementContext ctx) {
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();
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);
theme.setState(null);
renderScope.drawText(0.8f, ">", width - 10, 0, Theme.FONT.value, Theme.FOREGROUND.value);
}
@Override
@@ -1,22 +1,12 @@
package org.saturnclient.ui2.components.inputs;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
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;
public Toggle(Property<Boolean> prop) {
@@ -27,19 +17,10 @@ public class Toggle extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
theme.setState(null);
if (prop.value) {
theme.setState("enabled");
}
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);
renderScope.drawRoundedRectangle(0, 0, width, height, height,
Theme.withAlpha(0.5f, prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value));
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30,
prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value);
}
@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;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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 Runnable onClick;
public Button(String text, Runnable onClick) {
this.text = text;
this.onClick = onClick;
this.dimensions(Fonts.getWidth(text, font.value) + 50, 38);
this.dimensions(Fonts.getWidth(text, Theme.FONT.value) + 50, 38);
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.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, font.value), Fonts.centerY(height), font.value, fgColor.value);
renderScope.drawText(text, Fonts.centerX(width, text, Theme.FONT.value), Fonts.centerY(height),
Theme.FONT.value,
ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
}
@Override
@@ -1,7 +1,6 @@
package org.saturnclient.ui2.elements;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
@@ -10,9 +9,6 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
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;
private Runnable onClick;
@@ -23,14 +19,9 @@ public class ClickableTexture extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else {
theme.setState(null);
}
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
@@ -1,7 +1,7 @@
package org.saturnclient.ui2.elements;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
@@ -10,11 +10,9 @@ import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures;
public class Notification extends Element {
private static ThemeManager theme = new ThemeManager("Notification");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
private static Property<Integer> error = theme.property("error", Property.color(0xFFbf212f));
private static Property<Integer> info = theme.property("info", Property.color(0xFF264b96));
private static Property<Integer> success = theme.property("success", Property.color(0xFF27b376));
private static Property<Integer> error = Property.color(0xFFbf212f);
private static Property<Integer> info = Property.color(0xFF264b96);
private static Property<Integer> success = Property.color(0xFF27b376);
public static enum NotificationKind {
Error,
@@ -40,7 +38,7 @@ public class Notification extends Element {
@Override
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) {
case Error:
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 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.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.components.ElementRenderer;
public class Scroll extends Element {
private static ThemeManager theme = new ThemeManager("Scroll");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
private static Property<Integer> scrollBarColor = theme.property("scrollbar-color", Property.color(-7643914));
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));
private static Property<Integer> scrollBarRadius = Property.integer(10);
private static Property<Integer> scrollBarWidth = Property.integer(5);
private static Property<Integer> scrollBarPadding = Property.integer(5);
int padding = 0;
@@ -30,18 +25,37 @@ public class Scroll extends Element {
this.padding = padding;
}
public void draw(Element element) {
ElementRenderer.draw(children, element);
}
@Override
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.matrices.push();
renderScope.matrices.translate(padding, (-scroll) + padding, 0);
ElementRenderer.render(children, renderScope, ctx.mouseX - padding, ctx.mouseY - padding + scroll);
renderScope.matrices.translate(padding, -scroll + padding, 0);
ElementRenderer.render(children, ctx.elapsed, renderScope,
ctx.mouseX - padding,
ctx.mouseY - padding + scroll);
renderScope.matrices.pop();
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
@@ -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
public Element dimensions(int width, int height) {
maxScroll = 0;
for (Element element : children) {
maxScroll = Math.max(maxScroll, element.y + element.height - height);
}
maxScroll = Math.max(0, maxScroll); // never negative
return super.dimensions(width, height);
}
int calculateScrollBarHeight() {
if (maxScroll <= 0) return height - (scrollBarPadding.value * 2); // No scrolling needed
return Math.max(20, (height * height) / (height + maxScroll)) - (scrollBarPadding.value * 2); // At least 20px tall
if (maxScroll <= 0)
return height - (scrollBarPadding.value * 2);
return Math.max(20, (height * height) / (height + maxScroll + 10)) - (scrollBarPadding.value * 2);
}
int calculateScrollBarY() {
int scrollBarHeight = calculateScrollBarHeight();
if (maxScroll <= 0) return scrollBarPadding.value;
if (maxScroll <= 0)
return 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
public void keyPressed(int keyCode, int scanCode, int 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;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
@@ -10,16 +9,6 @@ import org.saturnclient.ui2.Utils;
import net.minecraft.util.Identifier;
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 {
Runnable onClick;
Identifier sprite;
@@ -42,19 +31,21 @@ public class TabMenu extends Element {
@Override
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;
for (TabMenuComponent component : components) {
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, iconFg.value);
renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10,
hovering ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
theme.setState(null);
col++;
}
}
@@ -1,27 +1,22 @@
package org.saturnclient.ui2.elements;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
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;
public Text(String text) {
this.text = text;
width = Fonts.getWidth(text, font.value);
width = Fonts.getWidth(text, Theme.FONT.value);
height = Fonts.getHeight();
}
@Override
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;
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.ElementContext;
import org.saturnclient.ui2.RenderScope;
@@ -10,19 +9,9 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
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;
private Runnable onClick;
static int padding = 14;
public TextureButton(Identifier sprite, Runnable onClick) {
this.sprite = sprite;
@@ -31,20 +20,16 @@ public class TextureButton extends Element {
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding.value;
int texHeight = height - padding.value;
int texWidth = width - padding;
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
@@ -4,6 +4,7 @@ import java.util.Map;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.components.Sidebar;
import org.saturnclient.ui2.components.inputs.FloatInput;
@@ -59,14 +60,13 @@ public class ConfigEditor extends SaturnScreen {
if (prop.getType() == Property.PropertyType.NAMESPACE) {
configScroll.draw(
new Text(
propName
).position(Fonts.centerX(520, propName, Text.font.value), modY + 1).scale(0.7f));
new Text(
propName).position(Fonts.centerX(520, propName, Theme.FONT.value), modY + 1)
.scale(0.7f));
} else {
configScroll.draw(
new Text(
propName
).position(modX, modY + 1).scale(0.7f));
new Text(
propName).position(modX, modY + 1).scale(0.7f));
}
switch (prop.getType()) {
@@ -78,7 +78,7 @@ public class ConfigEditor extends SaturnScreen {
break;
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;
case INTEGER:
@@ -1,8 +1,8 @@
package org.saturnclient.ui2.screens;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.config.SaturnClientConfig;
import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.Utils;
@@ -3,8 +3,10 @@ package org.saturnclient.ui2.screens;
import org.saturnclient.modules.ModManager;
import org.saturnclient.modules.Module;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.anim.SlideFade;
import org.saturnclient.ui2.components.SaturnModule;
import org.saturnclient.ui2.components.Sidebar;
import org.saturnclient.ui2.elements.AnimationStagger;
import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.elements.Scroll;
import org.saturnclient.ui2.resources.Textures;
@@ -24,8 +26,11 @@ public class ModMenu extends SaturnScreen {
int row = 0;
int col = 0;
AnimationStagger moduleStagger = new AnimationStagger(80);
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) {
col = 0;
@@ -35,6 +40,8 @@ public class ModMenu extends SaturnScreen {
}
}
scroll.draw(moduleStagger);
int scrollWidth = 480 + 10 + (g * 2) + (p * 2);
draw(scroll.dimensions(scrollWidth, 350).center(width, height));
@@ -1,12 +1,12 @@
package org.saturnclient.ui2.screens;
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.ui2.resources.Textures;
import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.elements.AnimationStagger;
import org.saturnclient.ui2.elements.Button;
import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.elements.TextureButton;
@@ -20,21 +20,42 @@ public class ShiftMenu extends SaturnScreen {
@Override
public void ui() {
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -36).animation(new Fade(700)));
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -80).animation(new SlideY(700, -20)));
// Logo animations (unchanged)
draw(new ImageTexture(Textures.LOGO_TEXT)
.dimensions(98, 10)
.centerOffset(width, height, 0, -36)
.animation(new Fade(700)));
Element button = new Button("Settings", () -> {
SaturnClient.client.setScreen(new ModMenu());
}).center(width, height);
draw(new ImageTexture(SaturnClientConfig.getLogo())
.dimensions(98, 98)
.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, () -> {
client.setScreen(new CloakMenu());
}).dimensions(button.height, button.height).centerOffset(width, height, (button.width / 2) + (button.height / 2) + 4, 0));
// Create stagger
AnimationStagger stagger = new AnimationStagger(120);
draw(new TextureButton(Textures.HUD_ICON, () -> {
stagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
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;
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.ui2.resources.Textures;
import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.anim.Fade;
import org.saturnclient.ui2.anim.SlideFade;
import org.saturnclient.ui2.anim.SlideY;
import org.saturnclient.ui2.elements.AnimationStagger;
import org.saturnclient.ui2.elements.Button;
import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.elements.TextureButton;
@@ -19,7 +21,7 @@ import net.minecraft.client.gui.screen.world.SelectWorldScreen;
public class TitleMenu extends SaturnScreen {
public TitleMenu() {
super("Tile Menu");
super("Title Menu");
}
@Override
@@ -31,34 +33,42 @@ public class TitleMenu extends SaturnScreen {
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -82)
.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()));
}).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()));
}).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));
}).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();
}).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;
draw(new TextureButton(Textures.HUD_ICON, () -> {
AnimationStagger saturnStagger = new AnimationStagger(180);
saturnStagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
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());
}).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());
}).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.client.ServiceClient;
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.Utils;
@@ -75,10 +76,10 @@ public class CloakStore extends SaturnScreen {
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)
.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) {
col = 0;
@@ -3,6 +3,7 @@ package org.saturnclient.ui2.screens.store;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.client.ServiceClient;
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.saturnclient.config.Theme;
import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.Utils;
@@ -75,10 +76,10 @@ public class HatStore extends SaturnScreen {
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)
.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) {
col = 0;