From dfebd2367d49a77f8765377d9272eff3768bdbd2 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sun, 27 Sep 2026 16:59:51 +0200 Subject: [PATCH] Redesign the mod menu The mod menu is now a translucent panel beside a navigation rail, with a header showing how many mods are enabled, a search box, category filters built from the mods' tags and a three-column grid of mod cards. Each card shows the mod's icon, name and description with a settings button and an on/off switch. The layout shrinks to fit smaller windows. New reusable pieces: Panel, Label, ChipGroup, SearchInput, Toggle.drawSwitch, Theme helpers for panel, border, muted text and body font, and Fonts.ellipsize. The sidebar and card icons are Lucide SVGs, and seven mod icons were redrawn in the same style. Scroll can skip its background and be cleared, and no longer passes clicks outside its bounds to children scrolled out of view. Input checks overflow and places the cursor at the scale its text is drawn at. Screens that are not redesigned yet move the wider sidebar so it keeps its gap. Co-Authored-By: Claude Opus 5.5 --- .../java/org/saturnclient/config/Theme.java | 29 +++ .../ui/components/SaturnModule.java | 195 +++++++----------- .../saturnclient/ui/components/Sidebar.java | 159 +++++++------- .../ui/components/inputs/Input.java | 28 ++- .../ui/components/inputs/SearchInput.java | 79 +++++++ .../ui/components/inputs/Toggle.java | 20 ++ .../saturnclient/ui/elements/ChipGroup.java | 85 ++++++++ .../org/saturnclient/ui/elements/Label.java | 33 +++ .../org/saturnclient/ui/elements/Panel.java | 22 ++ .../org/saturnclient/ui/elements/Scroll.java | 33 ++- .../org/saturnclient/ui/resources/Fonts.java | 18 ++ .../saturnclient/ui/resources/Textures.java | 9 + .../saturnclient/ui/screens/ConfigEditor.java | 2 +- .../org/saturnclient/ui/screens/ModMenu.java | 161 ++++++++++++--- .../ui/screens/cosmetics/CloakMenu.java | 4 +- .../ui/screens/cosmetics/HatMenu.java | 4 +- .../ui/screens/store/CloakStore.java | 4 +- .../ui/screens/store/HatStore.java | 4 +- .../textures/gui/components/close.svg | 1 + .../textures/gui/components/cosmetics.svg | 1 + .../textures/gui/components/hud_editor.svg | 1 + .../textures/gui/components/mods.svg | 1 + .../textures/gui/components/search.svg | 1 + .../textures/gui/components/settings.svg | 1 + .../textures/gui/components/sliders.svg | 1 + .../saturnclient/textures/gui/mod/clock.svg | 8 +- .../textures/gui/mod/crosshair.svg | 2 +- .../textures/gui/mod/nametags.svg | 8 +- .../saturnclient/textures/gui/mod/ping.svg | 8 +- .../saturnclient/textures/gui/mod/sprint.svg | 36 +--- .../saturnclient/textures/gui/mod/tps.svg | 8 +- .../saturnclient/textures/gui/mod/zoom.svg | 2 +- 32 files changed, 669 insertions(+), 299 deletions(-) create mode 100644 src/main/java/org/saturnclient/ui/components/inputs/SearchInput.java create mode 100644 src/main/java/org/saturnclient/ui/elements/ChipGroup.java create mode 100644 src/main/java/org/saturnclient/ui/elements/Label.java create mode 100644 src/main/java/org/saturnclient/ui/elements/Panel.java create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/close.svg create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/cosmetics.svg create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/hud_editor.svg create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/mods.svg create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/search.svg create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/settings.svg create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/sliders.svg diff --git a/src/main/java/org/saturnclient/config/Theme.java b/src/main/java/org/saturnclient/config/Theme.java index 70ad3af..8a9af41 100644 --- a/src/main/java/org/saturnclient/config/Theme.java +++ b/src/main/java/org/saturnclient/config/Theme.java @@ -64,6 +64,35 @@ public class Theme { return hover ? ACCENT_FG.value : PRIMARY_FG.value; } + /** + * Translucent background for panels drawn over the world + */ + public static int getPanelBg() { + return withAlpha(0.82f, BACKGROUND.value); + } + + /** + * Faint outline for panels, cards and inputs + */ + public static int getBorder() { + return withAlpha(0.08f, FOREGROUND.value); + } + + /** + * Secondary text: descriptions, counts and placeholders + */ + public static int getMutedFg() { + return withAlpha(0.65f, FOREGROUND.value); + } + + /** + * Font for secondary text. Inter bold is too heavy there, so it becomes + * regular Inter; other fonts are kept. + */ + public static int getBodyFont() { + return FONT.value == 2 ? 1 : FONT.value; + } + /** * Multiplies existing alpha with 0–255 alpha. */ diff --git a/src/main/java/org/saturnclient/ui/components/SaturnModule.java b/src/main/java/org/saturnclient/ui/components/SaturnModule.java index 3a753e3..1af513f 100644 --- a/src/main/java/org/saturnclient/ui/components/SaturnModule.java +++ b/src/main/java/org/saturnclient/ui/components/SaturnModule.java @@ -7,141 +7,100 @@ import org.saturnclient.mod.Mod; import org.saturnclient.ui.Element; import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.RenderScope; -import org.saturnclient.ui.Utils; +import org.saturnclient.ui.components.inputs.Toggle; import org.saturnclient.ui.resources.Fonts; import org.saturnclient.ui.resources.Textures; import org.saturnclient.ui.screens.ConfigEditor; +/** + * A mod's card in the mod menu: icon, name, description, a settings button and + * an on/off switch. Clicking anywhere but the settings button toggles the mod. + */ public class SaturnModule extends Element { + public static final int HEIGHT = 76; + + private static final int PADDING = 12; + private static final int ICON_TILE = 44; + private static final int ICON = 22; + private static final int SETTINGS = 32; + private static final int SETTINGS_ICON = 17; + private static final int GAP = 12; + private static final float NAME_SCALE = 0.74f; + private static final float DESCRIPTION_SCALE = 0.64f; private final Mod mod; - static int p = 10; // padding - static int s = 14; // settings icon size - static int expand = 5; // settings icon click area - - public SaturnModule(Mod mod) { + public SaturnModule(Mod mod, int width) { this.mod = mod; - this.width = 160; - this.height = 50; + this.width = width; + this.height = HEIGHT; } @Override public void render(RenderScope renderScope, ElementContext ctx) { - boolean hovered = ctx.isHovering(); boolean enabled = mod.isEnabled(); + boolean settingsHovered = ctx.isHovering(getSettingsX(), getSettingsY(), SETTINGS, SETTINGS); + boolean hovered = ctx.isHovering() && !settingsHovered; - int iconX = width - s - p; - int iconY = height - s - p; + int border = enabled ? Theme.withAlpha(hovered ? 0.7f : 0.4f, Theme.ACCENT.value) + : Theme.withAlpha(hovered ? 0.22f : 0.06f, Theme.FOREGROUND.value); - boolean settingsHover = ctx.isHovering( - getSettingsX(), - getSettingsY(), - getSettingsHitBox(), - getSettingsHitBox()); + // Card + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, + Theme.withAlpha(0.72f, Theme.PRIMARY.value), border); - // Resolve theme colors - int bgColor = Theme.PRIMARY.value; - int fgColor = hovered && !settingsHover ? Theme.ACCENT.value : Theme.PRIMARY_FG.value; - - int iconBg = enabled ? Theme.ACCENT.value : Theme.PRIMARY.value; - int iconFg = enabled ? Theme.ACCENT_FG.value : fgColor; - - int settingsColor = settingsHover - ? Theme.ACCENT.value - : Theme.withAlpha(160, fgColor); - - int h = height - (p * 2); - - // Main background - renderScope.drawRoundedRectangle( - 0, 0, - width, height, - Theme.WIDGET_RADIUS.value, - bgColor); - - // Icon background - renderScope.drawRoundedRectangle( - p, p, - h, h, - Theme.WIDGET_RADIUS.value, - iconBg); - - // Icon - renderScope.drawTexture( - mod.getIconTexture(), - p + (p / 2), - p + (p / 2), - 0, 0, - h - p, - h - p, - iconFg); - - // 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 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 (enabled) { + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, + Theme.withAlpha(0.06f, Theme.ACCENT.value)); } - // Settings icon - renderScope.drawTexture( - Textures.SETTINGS, - iconX, - iconY, - 0, 0, - s, s, - settingsColor); + // Icon + int tileY = (height - ICON_TILE) / 2; + renderScope.drawRoundedRectangle(PADDING, tileY, ICON_TILE, ICON_TILE, Theme.WIDGET_RADIUS.value, + enabled ? Theme.withAlpha(0.18f, Theme.ACCENT.value) : Theme.withAlpha(0.06f, Theme.FOREGROUND.value)); + + renderScope.drawTexture(mod.getIconTexture(), + PADDING + (ICON_TILE - ICON) / 2, tileY + (ICON_TILE - ICON) / 2, + 0, 0, ICON, ICON, Theme.PRIMARY_FG.value); + + // Name and description + int textX = PADDING + ICON_TILE + GAP; + int textWidth = getSettingsX() - GAP - textX; + int nameHeight = (int) (Fonts.getHeight() * NAME_SCALE); + int descriptionHeight = (int) (Fonts.getHeight() * DESCRIPTION_SCALE); + int nameY = (height - nameHeight - 5 - descriptionHeight) / 2; + + renderScope.drawText(NAME_SCALE, + Fonts.ellipsize(mod.getName(), Theme.FONT.value, NAME_SCALE, textWidth), + textX, nameY, Theme.FONT.value, Theme.FOREGROUND.value); + + String description = mod.getDescription() == null ? "" : mod.getDescription(); + renderScope.drawText(DESCRIPTION_SCALE, + Fonts.ellipsize(description, Theme.getBodyFont(), DESCRIPTION_SCALE, textWidth), + textX, nameY + nameHeight + 5, Theme.getBodyFont(), Theme.getMutedFg()); + + // Settings button + if (settingsHovered) { + renderScope.drawRoundedRectangle(getSettingsX(), getSettingsY(), SETTINGS, SETTINGS, + Theme.WIDGET_RADIUS.value, Theme.withAlpha(0.08f, Theme.FOREGROUND.value)); + } + + int settingsIconOffset = (SETTINGS - SETTINGS_ICON) / 2; + renderScope.drawTexture(Textures.ICON_SLIDERS, + getSettingsX() + settingsIconOffset, getSettingsY() + settingsIconOffset, + 0, 0, SETTINGS_ICON, SETTINGS_ICON, + settingsHovered ? Theme.ACCENT.value : Theme.getMutedFg()); + + // Switch + Toggle.drawSwitch(renderScope, getSwitchX(), (height - Toggle.SWITCH_HEIGHT) / 2, enabled); } @Override public void click(int mouseX, int mouseY) { - boolean settingsHover = Utils.isHovering( - mouseX - getSettingsX(), - mouseY - getSettingsY(), - getSettingsHitBox(), - getSettingsHitBox(), - 1.0f); + boolean settingsClicked = mouseX >= getSettingsX() && mouseX <= getSettingsX() + SETTINGS + && mouseY >= getSettingsY() && mouseY <= getSettingsY() + SETTINGS; - if (settingsHover) { + if (settingsClicked) { if (mod instanceof HudMod h) { Providers.saturn.getClient().setScreen(new ConfigEditor(mod.getConfig(), h)); } else { @@ -152,15 +111,15 @@ public class SaturnModule extends Element { } } + private int getSwitchX() { + return width - PADDING - Toggle.SWITCH_WIDTH; + } + private int getSettingsX() { - return width - s - p - expand; + return getSwitchX() - 8 - SETTINGS; } private int getSettingsY() { - return height - s - p - expand; + return (height - SETTINGS) / 2; } - - private int getSettingsHitBox() { - return s + expand * 2; - } -} \ No newline at end of file +} diff --git a/src/main/java/org/saturnclient/ui/components/Sidebar.java b/src/main/java/org/saturnclient/ui/components/Sidebar.java index 006b5a5..e263189 100644 --- a/src/main/java/org/saturnclient/ui/components/Sidebar.java +++ b/src/main/java/org/saturnclient/ui/components/Sidebar.java @@ -7,51 +7,48 @@ import org.saturnclient.config.Theme; import org.saturnclient.ui.Element; import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.RenderScope; -import org.saturnclient.ui.Utils; +import org.saturnclient.ui.elements.Panel; import org.saturnclient.ui.resources.Textures; import org.saturnclient.ui.screens.ConfigEditor; +import org.saturnclient.ui.screens.HudEditor; import org.saturnclient.ui.screens.ModMenu; import org.saturnclient.ui.screens.cosmetics.CloakMenu; -import org.saturnclient.ui.screens.store.CloakStore; +/** + * The navigation rail beside Saturn's screens: the logo, one button per screen, + * and a close button at the bottom. + */ public class Sidebar extends Element { - public static class SidebarComponent { - Runnable onClick; - boolean end; - IdentifierRef sprite; + public static final int WIDTH = 60; - public SidebarComponent(IdentifierRef sprite, Runnable onClick, boolean end) { - this.onClick = onClick; - this.end = end; - this.sprite = sprite; - } + // Values for the constructor's `active` + public static final int MODS = 0; + public static final int COSMETICS = 1; + public static final int HUD_EDITOR = 2; + public static final int SETTINGS = 3; + + private static final int LOGO = 32; + private static final int BUTTON = 44; + private static final int ICON = 22; + private static final int GAP = 8; + private static final int PADDING = 12; + + private record Item(IdentifierRef icon, Runnable onClick) { } - Runnable onClose; - SidebarComponent[] components = { - new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> { - Providers.saturn.getClient().setScreen(new ModMenu()); - }, false), - new Sidebar.SidebarComponent(Textures.SETTINGS, () -> { - Providers.saturn.getClient().setScreen(new ConfigEditor(Config.config)); - }, false), - - new Sidebar.SidebarComponent(Textures.SHIRT, () -> { - Providers.saturn.getClient().setScreen(new CloakMenu()); - }, false), - - new Sidebar.SidebarComponent(Textures.CLOSE, () -> { - onClose.run(); - }, true), - - new Sidebar.SidebarComponent(Textures.STORE, () -> { - Providers.saturn.getClient().setScreen(new CloakStore()); - }, true), + private final Item[] items = { + new Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())), + new Item(Textures.ICON_COSMETICS, () -> Providers.saturn.getClient().setScreen(new CloakMenu())), + new Item(Textures.ICON_HUD_EDITOR, () -> Providers.saturn.getClient().setScreen(new HudEditor())), + new Item(Textures.ICON_SETTINGS, + () -> Providers.saturn.getClient().setScreen(new ConfigEditor(Config.config))), }; - int active = 0; + + private final int active; + private final Runnable onClose; public Sidebar(int active, Runnable onClose) { - this.width = 30; + this.width = WIDTH; this.height = 350; this.active = active; this.onClose = onClose; @@ -59,63 +56,75 @@ public class Sidebar extends Element { @Override public void render(RenderScope renderScope, ElementContext ctx) { - int topRow = 0; - int bottomRow = 0; + Panel.drawBackground(renderScope, width, height); - int gap = 5; // 5px gap between icons - int padding = 10; - int sp = 30 - padding + gap; - int s = 30 - (padding * 2); - int s2 = s + padding; + renderScope.drawTexture(Textures.LOGO, (width - LOGO) / 2, PADDING, 0, 0, LOGO, LOGO); - renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Theme.BACKGROUND.value); + int x = getButtonX(); - int i = 0; + for (int i = 0; i < items.length; i++) { + int y = getItemY(i); + boolean selected = i == active; + boolean hovered = ctx.isHovering(x, y, BUTTON, BUTTON); - 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) { - bottomRow += 1; - } else { - topRow += 1; + if (selected) { + renderScope.drawRoundedRectangle(x, y, BUTTON, BUTTON, Theme.WIDGET_RADIUS.value, Theme.ACCENT.value); + } else if (hovered) { + renderScope.drawRoundedRectangle(x, y, BUTTON, BUTTON, Theme.WIDGET_RADIUS.value, + Theme.withAlpha(0.08f, Theme.FOREGROUND.value)); } - i++; + drawIcon(renderScope, items[i].icon(), x, y, + selected ? Theme.ACCENT_FG.value + : hovered ? Theme.FOREGROUND.value : Theme.withAlpha(0.8f, Theme.FOREGROUND.value)); } + + int closeY = getCloseY(); + boolean closeHovered = ctx.isHovering(x, closeY, BUTTON, BUTTON); + + renderScope.drawRoundedRectangle(x, closeY, BUTTON, BUTTON, Theme.WIDGET_RADIUS.value, + Theme.withAlpha(closeHovered ? 0.12f : 0.06f, Theme.FOREGROUND.value)); + + drawIcon(renderScope, Textures.ICON_CLOSE, x, closeY, + closeHovered ? Theme.FOREGROUND.value : Theme.withAlpha(0.8f, Theme.FOREGROUND.value)); } @Override public void click(int mouseX, int mouseY) { - int topRow = 0; - int bottomRow = 0; + int x = getButtonX(); - int gap = 5; - int padding = 10; - int sp = 30 - padding + gap; - int s = 30 - (padding * 2); - int s2 = s + padding; + if (mouseX < x || mouseX > x + BUTTON) { + return; + } - 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); + for (int i = 0; i < items.length; i++) { + int y = getItemY(i); - if (hovering) { - component.onClick.run(); - break; - } - - if (component.end) { - bottomRow += 1; - } else { - topRow += 1; + if (mouseY >= y && mouseY <= y + BUTTON) { + items[i].onClick().run(); + return; } } + + if (mouseY >= getCloseY() && mouseY <= getCloseY() + BUTTON) { + onClose.run(); + } + } + + private static void drawIcon(RenderScope renderScope, IdentifierRef icon, int x, int y, int color) { + int offset = (BUTTON - ICON) / 2; + renderScope.drawTexture(icon, x + offset, y + offset, 0, 0, ICON, ICON, color); + } + + private int getButtonX() { + return (width - BUTTON) / 2; + } + + private int getItemY(int index) { + return PADDING + LOGO + 14 + index * (BUTTON + GAP); + } + + private int getCloseY() { + return height - PADDING - BUTTON; } } diff --git a/src/main/java/org/saturnclient/ui/components/inputs/Input.java b/src/main/java/org/saturnclient/ui/components/inputs/Input.java index 8947416..421c759 100644 --- a/src/main/java/org/saturnclient/ui/components/inputs/Input.java +++ b/src/main/java/org/saturnclient/ui/components/inputs/Input.java @@ -11,6 +11,11 @@ public abstract class Input extends Element { String text = ""; public int cursorPosition = 0; + /** Scale the text is drawn at */ + protected float textScale = 0.6f; + /** Horizontal inset of the text on both sides */ + protected int textX = 4; + public abstract void checkReset(); public abstract void backspace(); @@ -41,6 +46,7 @@ public abstract class Input extends Element { @Override public void click(int mouseX, int mouseY) { + mouseX -= textX; int scrollOffset = getScrollOffset(); String visibleText = text.substring(scrollOffset); @@ -48,7 +54,7 @@ public abstract class Input extends Element { float textWidth = 0; for (int i = 0; i < visibleText.length(); i++) { - textWidth = Fonts.getWidth(visibleText.substring(0, i + 1), Theme.FONT.value) * 0.6f; + textWidth = Fonts.getWidth(visibleText.substring(0, i + 1), Theme.FONT.value) * textScale; if (textWidth > mouseX) { break; @@ -68,24 +74,32 @@ public abstract class Input extends Element { int scrollOffset = getScrollOffset(); String visibleText = getVisibleText(scrollOffset); - renderScope.drawText(0.6f, visibleText, 4, 4, Theme.FONT.value, Theme.PRIMARY_FG.value); + renderScope.drawText(textScale, visibleText, textX, 4, Theme.FONT.value, Theme.PRIMARY_FG.value); if (focused) { - int cursorX = 4 + - (int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), Theme.FONT.value) - * 0.6f); + int cursorX = getCursorX(visibleText, scrollOffset); renderScope.drawRectangle(cursorX, 2, 1, height - 4, Theme.ACCENT.value); } } + protected int getCursorX(String visibleText, int scrollOffset) { + return textX + (int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), + Theme.FONT.value) * textScale); + } + + /** Width available to the text, in unscaled font units */ + private float getTextSpace() { + return (width - textX * 2) / textScale; + } + protected int getScrollOffset() { if (text.isEmpty()) { return 0; } int offset = 0; - int maxWidth = width - 4; // Available width for text + float maxWidth = getTextSpace(); while (offset < cursorPosition) { // Ensure cursor remains visible String visibleText = text.substring(offset, cursorPosition); if (Fonts.getWidth(visibleText, Theme.FONT.value) > maxWidth) { @@ -102,7 +116,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), Theme.FONT.value) > width - 4) { + if (Fonts.getWidth(visibleText.substring(0, i), Theme.FONT.value) > getTextSpace()) { return visibleText.substring(0, i - 1); // Trim overflow } } diff --git a/src/main/java/org/saturnclient/ui/components/inputs/SearchInput.java b/src/main/java/org/saturnclient/ui/components/inputs/SearchInput.java new file mode 100644 index 0000000..f515408 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/components/inputs/SearchInput.java @@ -0,0 +1,79 @@ +package org.saturnclient.ui.components.inputs; + +import java.util.function.Consumer; + +import org.saturnclient.config.Theme; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.resources.Fonts; +import org.saturnclient.ui.resources.Textures; + +/** + * A free-text search box with an icon and placeholder, reporting every change. + */ +public class SearchInput extends Input { + private static final int ICON_SIZE = 16; + private static final int ICON_X = 12; + + private final String placeholder; + private final Consumer onChange; + + public SearchInput(String placeholder, Consumer onChange) { + this.placeholder = placeholder; + this.onChange = onChange; + this.textScale = 0.68f; + this.textX = ICON_X + ICON_SIZE + 8; + this.width = 240; + this.height = 36; + } + + @Override + public void charTyped(char chr) { + if (text.length() >= 64) { + return; + } + + text = text.substring(0, cursorPosition) + chr + text.substring(cursorPosition); + cursorPosition++; + onChange.accept(text); + } + + @Override + public void backspace() { + onChange.accept(text); + } + + @Override + public void checkReset() { + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + int border = focused ? Theme.withAlpha(0.6f, Theme.ACCENT.value) + : ctx.isHovering() ? Theme.withAlpha(0.25f, Theme.FOREGROUND.value) + : Theme.withAlpha(0.1f, Theme.FOREGROUND.value); + + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, + Theme.withAlpha(0.06f, Theme.FOREGROUND.value), border); + + renderScope.drawTexture(Textures.ICON_SEARCH, ICON_X, (height - ICON_SIZE) / 2, 0, 0, + ICON_SIZE, ICON_SIZE, Theme.getMutedFg()); + + int textHeight = (int) (Fonts.getHeight() * textScale); + int textY = (height - textHeight) / 2; + + if (text.isEmpty() && !focused) { + renderScope.drawText(textScale, placeholder, textX, textY, Theme.getBodyFont(), Theme.getMutedFg()); + return; + } + + int scrollOffset = getScrollOffset(); + String visibleText = getVisibleText(scrollOffset); + renderScope.drawText(textScale, visibleText, textX, textY, Theme.FONT.value, Theme.FOREGROUND.value); + + if (focused) { + renderScope.drawRectangle(getCursorX(visibleText, scrollOffset), textY - 1, 1, textHeight + 2, + Theme.ACCENT.value); + } + } +} diff --git a/src/main/java/org/saturnclient/ui/components/inputs/Toggle.java b/src/main/java/org/saturnclient/ui/components/inputs/Toggle.java index c55f940..0987723 100644 --- a/src/main/java/org/saturnclient/ui/components/inputs/Toggle.java +++ b/src/main/java/org/saturnclient/ui/components/inputs/Toggle.java @@ -23,6 +23,26 @@ public class Toggle extends Element { prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value); } + public static final int SWITCH_WIDTH = 42; + public static final int SWITCH_HEIGHT = 24; + + /** + * Draws an on/off switch: a pill track with a round knob on the right when on. + */ + public static void drawSwitch(RenderScope renderScope, int x, int y, boolean on) { + int inset = 3; + int knob = SWITCH_HEIGHT - inset * 2; + + renderScope.drawRoundedRectangle(x, y, SWITCH_WIDTH, SWITCH_HEIGHT, SWITCH_HEIGHT / 2, + on ? Theme.ACCENT.value : Theme.withAlpha(0.16f, Theme.FOREGROUND.value)); + + renderScope.drawRoundedRectangle( + on ? x + SWITCH_WIDTH - inset - knob : x + inset, + y + inset, + knob, knob, knob / 2, + on ? Theme.ACCENT_FG.value : Theme.withAlpha(0.8f, Theme.PRIMARY_FG.value)); + } + @Override public void click(int mouseX, int mouseY) { prop.value = !prop.value; diff --git a/src/main/java/org/saturnclient/ui/elements/ChipGroup.java b/src/main/java/org/saturnclient/ui/elements/ChipGroup.java new file mode 100644 index 0000000..6f23659 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/ChipGroup.java @@ -0,0 +1,85 @@ +package org.saturnclient.ui.elements; + +import java.util.List; +import java.util.function.Consumer; + +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.resources.Fonts; + +/** + * A row of pill buttons where exactly one is selected, such as category filters. + */ +public class ChipGroup extends Element { + private static final int HEIGHT = 28; + private static final int PADDING = 14; + private static final int GAP = 8; + private static final float TEXT_SCALE = 0.62f; + + private final List options; + private final Consumer onSelect; + private String selected; + + public ChipGroup(List options, String selected, Consumer onSelect) { + this.options = options; + this.selected = selected; + this.onSelect = onSelect; + this.height = HEIGHT; + this.width = chipX(options.size()) - GAP; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + int font = Theme.FONT.value; + int textY = (HEIGHT - (int) (Fonts.getHeight() * TEXT_SCALE)) / 2; + + for (int i = 0; i < options.size(); i++) { + String option = options.get(i); + int x = chipX(i); + int w = chipWidth(option); + boolean active = option.equals(selected); + boolean hovered = ctx.isHovering(x, 0, w, HEIGHT); + + if (active) { + renderScope.drawRoundedRectangle(x, 0, w, HEIGHT, HEIGHT / 2, Theme.ACCENT.value); + } else { + renderScope.drawRoundedRectangle(x, 0, w, HEIGHT, HEIGHT / 2, 1, + Theme.withAlpha(0.05f, Theme.FOREGROUND.value), + hovered ? Theme.withAlpha(0.25f, Theme.FOREGROUND.value) : Theme.getBorder()); + } + + renderScope.drawText(TEXT_SCALE, option, x + PADDING, textY, font, + active ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value); + } + } + + @Override + public void click(int mouseX, int mouseY) { + for (int i = 0; i < options.size(); i++) { + String option = options.get(i); + int x = chipX(i); + + if (mouseX >= x && mouseX <= x + chipWidth(option)) { + if (!option.equals(selected)) { + selected = option; + onSelect.accept(option); + } + return; + } + } + } + + private int chipX(int index) { + int x = 0; + for (int i = 0; i < index; i++) { + x += chipWidth(options.get(i)) + GAP; + } + return x; + } + + private static int chipWidth(String option) { + return (int) (Fonts.getWidth(option, Theme.FONT.value) * TEXT_SCALE) + PADDING * 2; + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Label.java b/src/main/java/org/saturnclient/ui/elements/Label.java new file mode 100644 index 0000000..a0be75a --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/Label.java @@ -0,0 +1,33 @@ +package org.saturnclient.ui.elements; + +import java.util.function.IntSupplier; +import java.util.function.Supplier; + +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.resources.Fonts; + +/** + * A line of text whose content, font and color are read on every frame, so it + * follows state and theme changes without rebuilding the screen. + */ +public class Label extends Element { + private final Supplier text; + private final float textScale; + private final IntSupplier font; + private final IntSupplier color; + + public Label(Supplier text, float textScale, IntSupplier font, IntSupplier color) { + this.text = text; + this.textScale = textScale; + this.font = font; + this.color = color; + this.height = (int) (Fonts.getHeight() * textScale); + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawText(textScale, text.get(), 0, 0, font.getAsInt(), color.getAsInt()); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Panel.java b/src/main/java/org/saturnclient/ui/elements/Panel.java new file mode 100644 index 0000000..2b3369e --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/Panel.java @@ -0,0 +1,22 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; + +/** + * The translucent, outlined surface that screens lay their content on. + */ +public class Panel extends Element { + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + drawBackground(renderScope, width, height); + } + + public static void drawBackground(RenderScope renderScope, int width, int height) { + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, 1, + Theme.getPanelBg(), Theme.getBorder()); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Scroll.java b/src/main/java/org/saturnclient/ui/elements/Scroll.java index 31daaa3..fe6fcf3 100644 --- a/src/main/java/org/saturnclient/ui/elements/Scroll.java +++ b/src/main/java/org/saturnclient/ui/elements/Scroll.java @@ -17,6 +17,7 @@ public class Scroll extends Element { private static IntProperty scrollBarPadding = Property.integer(5); int padding = 0; + boolean background = true; protected List children = new ArrayList<>(); int scroll = 0; @@ -30,12 +31,31 @@ public class Scroll extends Element { ElementRenderer.draw(children, element); } + /** + * Skips the scroll's own background, for when it sits on a {@link Panel}. + */ + public Scroll noBackground() { + this.background = false; + return this; + } + + /** + * Removes every child and scrolls back to the top. + */ + public void clear() { + children.clear(); + scroll = 0; + maxScroll = 0; + } + @Override public void render(RenderScope renderScope, ElementContext ctx) { calculateMaxScroll(); - renderScope.drawRoundedRectangle(0, 0, width, height, - Theme.BG_RADIUS.value, Theme.BACKGROUND.value); + if (background) { + renderScope.drawRoundedRectangle(0, 0, width, height, + Theme.BG_RADIUS.value, Theme.BACKGROUND.value); + } renderScope.enableScissor(padding, padding, width - padding, height - padding); renderScope.getMatrixStack().push(); @@ -117,6 +137,15 @@ public class Scroll extends Element { @Override public void mouseClicked(double mouseX, double mouseY, int button) { + // Children scrolled out of view still have positions under the rest of the + // screen, so only clicks inside the scroll's own bounds may reach them. + if (mouseX < 0 || mouseY < 0 || mouseX > width || mouseY > height) { + for (Element child : children) { + child.focused = false; + } + return; + } + ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, button); } diff --git a/src/main/java/org/saturnclient/ui/resources/Fonts.java b/src/main/java/org/saturnclient/ui/resources/Fonts.java index 4c66770..def6d4b 100644 --- a/src/main/java/org/saturnclient/ui/resources/Fonts.java +++ b/src/main/java/org/saturnclient/ui/resources/Fonts.java @@ -44,6 +44,24 @@ public class Fonts { return 18; } + /** + * Shortens {@code text} with a trailing "…" so it fits in {@code maxWidth} + * when drawn at {@code scale}. + */ + public static String ellipsize(String text, int font, float scale, int maxWidth) { + if (getWidth(text, font) * scale <= maxWidth) { + return text; + } + + String ellipsis = "…"; + int end = text.length(); + while (end > 0 && getWidth(text.substring(0, end).stripTrailing() + ellipsis, font) * scale > maxWidth) { + end--; + } + + return text.substring(0, end).stripTrailing() + ellipsis; + } + public static int centerX(int w, String text, int font) { return (w - Fonts.getWidth(text, font)) / 2; } diff --git a/src/main/java/org/saturnclient/ui/resources/Textures.java b/src/main/java/org/saturnclient/ui/resources/Textures.java index 6058e95..68bb46b 100644 --- a/src/main/java/org/saturnclient/ui/resources/Textures.java +++ b/src/main/java/org/saturnclient/ui/resources/Textures.java @@ -25,6 +25,15 @@ public class Textures { public static final IdentifierRef INFO = IdentifierRef.ofSaturn("textures/gui/components/info.png"); public static final IdentifierRef CHECK = IdentifierRef.ofSaturn("textures/gui/components/check.png"); + // Lucide icons (SVG, drawn in white so they can be tinted) + public static final IdentifierRef ICON_MODS = IdentifierRef.ofSaturn("textures/gui/components/mods.svg"); + public static final IdentifierRef ICON_COSMETICS = IdentifierRef.ofSaturn("textures/gui/components/cosmetics.svg"); + public static final IdentifierRef ICON_HUD_EDITOR = IdentifierRef.ofSaturn("textures/gui/components/hud_editor.svg"); + public static final IdentifierRef ICON_SETTINGS = IdentifierRef.ofSaturn("textures/gui/components/settings.svg"); + public static final IdentifierRef ICON_CLOSE = IdentifierRef.ofSaturn("textures/gui/components/close.svg"); + public static final IdentifierRef ICON_SEARCH = IdentifierRef.ofSaturn("textures/gui/components/search.svg"); + public static final IdentifierRef ICON_SLIDERS = IdentifierRef.ofSaturn("textures/gui/components/sliders.svg"); + public static final IdentifierRef LOGO = IdentifierRef.ofSaturn("textures/logo/logo.png"); public static final IdentifierRef REALISTIC_LOGO = IdentifierRef.ofSaturn("textures/logo/realistic.png"); public static final IdentifierRef LOGO_TEXT = IdentifierRef.ofSaturn("textures/logo/text.png"); diff --git a/src/main/java/org/saturnclient/ui/screens/ConfigEditor.java b/src/main/java/org/saturnclient/ui/screens/ConfigEditor.java index e21245a..fb31348 100644 --- a/src/main/java/org/saturnclient/ui/screens/ConfigEditor.java +++ b/src/main/java/org/saturnclient/ui/screens/ConfigEditor.java @@ -63,7 +63,7 @@ public class ConfigEditor extends SaturnScreen { draw(scroll.dimensions(scrollWidth, 350).center(width, height)); - draw(new Sidebar(1, this.provider::close).center(width, height, -(scrollWidth / 2 + 20), 0)); + draw(new Sidebar(Sidebar.SETTINGS, this.provider::close).center(width, height, -(scrollWidth / 2 + 35), 0)); } private int drawProperties(Scroll configScroll, Map properties, int row, int col, int w) { diff --git a/src/main/java/org/saturnclient/ui/screens/ModMenu.java b/src/main/java/org/saturnclient/ui/screens/ModMenu.java index a531eca..ef6eadb 100644 --- a/src/main/java/org/saturnclient/ui/screens/ModMenu.java +++ b/src/main/java/org/saturnclient/ui/screens/ModMenu.java @@ -1,56 +1,171 @@ package org.saturnclient.ui.screens; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + import org.saturnclient.config.AnimationConfig; +import org.saturnclient.config.Theme; import org.saturnclient.mod.Mod; import org.saturnclient.mod.ModManager; import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.anim.SlideFade; import org.saturnclient.ui.components.SaturnModule; import org.saturnclient.ui.components.Sidebar; +import org.saturnclient.ui.components.inputs.SearchInput; import org.saturnclient.ui.elements.AnimationStagger; +import org.saturnclient.ui.elements.ChipGroup; import org.saturnclient.ui.elements.ImageTexture; +import org.saturnclient.ui.elements.Label; +import org.saturnclient.ui.elements.Panel; import org.saturnclient.ui.elements.Scroll; +import org.saturnclient.ui.resources.Fonts; import org.saturnclient.ui.resources.Textures; public class ModMenu extends SaturnScreen { + private static final String ALL = "All"; + + private static final int MAX_WIDTH = 900; + private static final int MAX_HEIGHT = 580; + private static final int MARGIN = 40; + private static final int GAP = 12; + private static final int PADDING = 20; + private static final int COLUMNS = 3; + private static final int CARD_GAP = 10; + private static final int SCROLLBAR_SPACE = 14; + + private String category = ALL; + private String query = ""; + + private Scroll scroll; + private int gridWidth; + public ModMenu() { super("Mod Menu"); } @Override public void ui() { - int p = 10; - int g = 10; + // The search box starts empty after a resize, so the query does too. + query = ""; - Scroll scroll = new Scroll(p); + int panelWidth = Math.min(MAX_WIDTH, width - MARGIN * 2 - Sidebar.WIDTH - GAP); + int panelHeight = Math.min(MAX_HEIGHT, height - MARGIN * 2); + int left = (width - (Sidebar.WIDTH + GAP + panelWidth)) / 2; + int top = (height - panelHeight) / 2; + int panelX = left + Sidebar.WIDTH + GAP; + int contentX = panelX + PADDING; + gridWidth = panelWidth - PADDING * 2; - int row = 0; - int col = 0; + draw(new Sidebar(Sidebar.MODS, this.provider::close) + .dimensions(Sidebar.WIDTH, panelHeight) + .position(left, top)); - AnimationStagger moduleStagger = new AnimationStagger(AnimationConfig.modMenu); + draw(new Panel().dimensions(panelWidth, panelHeight).position(panelX, top)); - for (Mod mod : ModManager.MODS) { - moduleStagger.draw( - new SaturnModule(mod) - .position((160 + g) * col, (50 + g) * row) - .animation(new SlideFade(AnimationConfig.modMenu, -12))); + // Header + draw(new Label(() -> "Mods", 1.15f, () -> Theme.FONT.value, () -> Theme.FOREGROUND.value) + .position(contentX, top + PADDING + 2)); - if (col == 2) { - col = 0; - row++; - } else { - col++; - } - } + draw(new Label(this::getEnabledSummary, 0.66f, Theme::getBodyFont, Theme::getMutedFg) + .position(contentX, top + PADDING + 28)); - scroll.draw(moduleStagger); + SearchInput search = new SearchInput("Search mods", text -> { + query = text; + refresh(false); + }); + draw(search.position(panelX + panelWidth - PADDING - search.width, top + PADDING + 2)); - int scrollWidth = 480 + 10 + (g * 2) + (p * 2); + // Categories + int chipsY = top + PADDING + 56; + ChipGroup categories = new ChipGroup(getCategories(), category, selected -> { + category = selected; + refresh(false); + }); + draw(categories.position(contentX, chipsY)); - draw(scroll.dimensions(scrollWidth, 350).center(width, height)); - - draw(new Sidebar(0, this.provider::close).center(width, height, -(scrollWidth / 2 + 20), 0)); + // Mods + int scrollY = chipsY + categories.height + 14; + scroll = new Scroll(0).noBackground(); + scroll.dimensions(gridWidth + SCROLLBAR_SPACE, top + panelHeight - 12 - scrollY); + refresh(true); + draw(scroll.position(contentX, scrollY)); draw(new ImageTexture(Textures.LOGO_TEXT_BIG).dimensions(180, 18).position(width - 180 - 20, height - 18 - 20)); } + + /** + * Fills the grid with the mods that match the category and search query. + */ + private void refresh(boolean animate) { + scroll.clear(); + + List mods = ModManager.MODS.stream().filter(this::matches).toList(); + + if (mods.isEmpty()) { + String message = "No mods match “" + query + "”"; + float scale = 0.7f; + int x = (gridWidth - (int) (Fonts.getWidth(message, Theme.getBodyFont()) * scale)) / 2; + + scroll.draw(new Label(() -> message, scale, Theme::getBodyFont, Theme::getMutedFg).position(x, 60)); + return; + } + + int cardWidth = (gridWidth - CARD_GAP * (COLUMNS - 1)) / COLUMNS; + AnimationStagger grid = new AnimationStagger(AnimationConfig.modMenu); + + for (int i = 0; i < mods.size(); i++) { + int col = i % COLUMNS; + int row = i / COLUMNS; + + SaturnModule card = new SaturnModule(mods.get(i), cardWidth); + card.position((cardWidth + CARD_GAP) * col, (SaturnModule.HEIGHT + CARD_GAP) * row); + + if (animate) { + card.animation(new SlideFade(AnimationConfig.modMenu, -12)); + } + + grid.draw(card); + } + + scroll.draw(grid); + } + + private boolean matches(Mod mod) { + if (!category.equals(ALL) && !List.of(mod.getTags()).contains(category)) { + return false; + } + + String q = query.trim().toLowerCase(Locale.ROOT); + if (q.isEmpty()) { + return true; + } + + String description = mod.getDescription() == null ? "" : mod.getDescription(); + return mod.getName().toLowerCase(Locale.ROOT).contains(q) + || description.toLowerCase(Locale.ROOT).contains(q); + } + + /** + * "All", then every mod tag in the order it first appears. + */ + private static List getCategories() { + List categories = new ArrayList<>(); + categories.add(ALL); + + for (Mod mod : ModManager.MODS) { + for (String tag : mod.getTags()) { + if (!categories.contains(tag)) { + categories.add(tag); + } + } + } + + return categories; + } + + private String getEnabledSummary() { + long enabled = ModManager.MODS.stream().filter(Mod::isEnabled).count(); + return enabled + " of " + ModManager.MODS.size() + " enabled"; + } } diff --git a/src/main/java/org/saturnclient/ui/screens/cosmetics/CloakMenu.java b/src/main/java/org/saturnclient/ui/screens/cosmetics/CloakMenu.java index db0b936..79699d5 100644 --- a/src/main/java/org/saturnclient/ui/screens/cosmetics/CloakMenu.java +++ b/src/main/java/org/saturnclient/ui/screens/cosmetics/CloakMenu.java @@ -68,8 +68,8 @@ public class CloakMenu extends SaturnScreen { .position(scroll.x + (scrollWidth - 220), scroll.y + 40) .animation(new Fade(500))); - draw(new Sidebar(2, this.provider::close) - .center(width, height, -(scrollWidth / 2 + 20), 0)); + draw(new Sidebar(Sidebar.COSMETICS, this.provider::close) + .center(width, height, -(scrollWidth / 2 + 35), 0)); draw(new TabMenu(0, new TabMenuComponent(Textures.CLOAK, () -> { diff --git a/src/main/java/org/saturnclient/ui/screens/cosmetics/HatMenu.java b/src/main/java/org/saturnclient/ui/screens/cosmetics/HatMenu.java index 6493e23..1d2244d 100644 --- a/src/main/java/org/saturnclient/ui/screens/cosmetics/HatMenu.java +++ b/src/main/java/org/saturnclient/ui/screens/cosmetics/HatMenu.java @@ -68,8 +68,8 @@ public class HatMenu extends SaturnScreen { .position(scroll.x + (scrollWidth - 220), scroll.y + 40) .animation(new Fade(500))); - draw(new Sidebar(2, this.provider::close) - .center(width, height, -(scrollWidth / 2 + 20), 0)); + draw(new Sidebar(Sidebar.COSMETICS, this.provider::close) + .center(width, height, -(scrollWidth / 2 + 35), 0)); draw(new TabMenu(1, new TabMenuComponent(Textures.CLOAK, () -> { diff --git a/src/main/java/org/saturnclient/ui/screens/store/CloakStore.java b/src/main/java/org/saturnclient/ui/screens/store/CloakStore.java index bc62890..c6c2e7d 100644 --- a/src/main/java/org/saturnclient/ui/screens/store/CloakStore.java +++ b/src/main/java/org/saturnclient/ui/screens/store/CloakStore.java @@ -98,8 +98,8 @@ public class CloakStore extends SaturnScreen { .dimensions(scrollWidth, 350) .center(width, height, 15, 0)); - draw(new Sidebar(5, this.provider::close) - .center(width, height, -((scrollWidth - 30) / 2 + 20), 0)); + draw(new Sidebar(Sidebar.COSMETICS, this.provider::close) + .center(width, height, -((scrollWidth - 30) / 2 + 35), 0)); draw(new TabMenu(0, new TabMenuComponent(Textures.CLOAK, () -> { diff --git a/src/main/java/org/saturnclient/ui/screens/store/HatStore.java b/src/main/java/org/saturnclient/ui/screens/store/HatStore.java index c2540ec..aec83e8 100644 --- a/src/main/java/org/saturnclient/ui/screens/store/HatStore.java +++ b/src/main/java/org/saturnclient/ui/screens/store/HatStore.java @@ -98,8 +98,8 @@ public class HatStore extends SaturnScreen { .dimensions(scrollWidth, 350) .center(width, height, 15, 0)); - draw(new Sidebar(5, this.provider::close) - .center(width, height, -((scrollWidth - 30) / 2 + 20), 0)); + draw(new Sidebar(Sidebar.COSMETICS, this.provider::close) + .center(width, height, -((scrollWidth - 30) / 2 + 35), 0)); draw(new TabMenu(1, new TabMenuComponent(Textures.CLOAK, () -> { diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/close.svg b/src/main/resources/assets/saturnclient/textures/gui/components/close.svg new file mode 100644 index 0000000..c509233 --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/close.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/cosmetics.svg b/src/main/resources/assets/saturnclient/textures/gui/components/cosmetics.svg new file mode 100644 index 0000000..8eca3bb --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/cosmetics.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/hud_editor.svg b/src/main/resources/assets/saturnclient/textures/gui/components/hud_editor.svg new file mode 100644 index 0000000..3978faf --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/hud_editor.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/mods.svg b/src/main/resources/assets/saturnclient/textures/gui/components/mods.svg new file mode 100644 index 0000000..65d3912 --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/mods.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/search.svg b/src/main/resources/assets/saturnclient/textures/gui/components/search.svg new file mode 100644 index 0000000..8c7fb8d --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/search.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/settings.svg b/src/main/resources/assets/saturnclient/textures/gui/components/settings.svg new file mode 100644 index 0000000..75ba448 --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/settings.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/sliders.svg b/src/main/resources/assets/saturnclient/textures/gui/components/sliders.svg new file mode 100644 index 0000000..a433070 --- /dev/null +++ b/src/main/resources/assets/saturnclient/textures/gui/components/sliders.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/clock.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/clock.svg index c928bd6..a260617 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/clock.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/clock.svg @@ -1,7 +1 @@ - - - - - - - \ No newline at end of file + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/crosshair.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/crosshair.svg index bb0cb50..572ac84 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/crosshair.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/crosshair.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/nametags.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/nametags.svg index 876a0f0..233afdd 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/nametags.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/nametags.svg @@ -1,7 +1 @@ - - - - - - - \ No newline at end of file + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/ping.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/ping.svg index 6eaafc9..a230b11 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/ping.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/ping.svg @@ -1,7 +1 @@ - - - - - - - \ No newline at end of file + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/sprint.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/sprint.svg index 35e91e7..11515b0 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/sprint.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/sprint.svg @@ -1,35 +1 @@ - - - - -Created by potrace 1.15, written by Peter Selinger 2001-2017 - - - - - - + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/tps.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/tps.svg index f2ad6c9..7c5fed6 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/tps.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/tps.svg @@ -1,7 +1 @@ - - - - - - performance_line - \ No newline at end of file + diff --git a/src/main/resources/assets/saturnclient/textures/gui/mod/zoom.svg b/src/main/resources/assets/saturnclient/textures/gui/mod/zoom.svg index 320a59d..e0c3ce8 100644 --- a/src/main/resources/assets/saturnclient/textures/gui/mod/zoom.svg +++ b/src/main/resources/assets/saturnclient/textures/gui/mod/zoom.svg @@ -1 +1 @@ - \ No newline at end of file +