package org.saturnclient.ui.screens; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.AnimationConfig; import org.saturnclient.config.Config; import org.saturnclient.config.Theme; import org.saturnclient.config.property.ColorProperty; import org.saturnclient.config.property.NamespaceProperty; import org.saturnclient.config.property.Property; import org.saturnclient.ui.PanelScreen; import org.saturnclient.ui.components.ColorGrid; import org.saturnclient.ui.components.PropertyList; import org.saturnclient.ui.components.SectionNav; import org.saturnclient.ui.components.Sidebar; import org.saturnclient.ui.components.ThemePresetCard; import org.saturnclient.ui.elements.ActionButton; import org.saturnclient.ui.elements.Divider; import org.saturnclient.ui.elements.Label; import org.saturnclient.ui.elements.Scroll; import org.saturnclient.ui.resources.Textures; /** * The client's own settings, split into sections picked from a list on the left. */ public class ClientSettings extends PanelScreen { private enum Section { GENERAL("General", "Title screen, emotes and cosmetics", Textures.ICON_SLIDERS), UI_STYLE("UI Style", "Colors, corners and font for every Saturn screen", Textures.ICON_PALETTE), ANIMATIONS("Animations", "How long menu animations take, and their easing", Textures.ICON_ANIMATIONS); final String title; final String description; final IdentifierRef icon; Section(String title, String description, IdentifierRef icon) { this.title = title; this.description = description; this.icon = icon; } } private static final int NAV_WIDTH = 170; private static final int NAV_GAP = 16; private static final int LIST_Y = 58; private static final int SECTION_GAP = 22; private static final int PRESET_GAP = 10; private Section section = Section.GENERAL; public ClientSettings() { super("Settings", Sidebar.SETTINGS); } @Override protected void panel() { List items = Stream.of(Section.values()) .map(s -> new SectionNav.Item(s.icon, s.title)) .toList(); draw(new SectionNav("SATURN CLIENT", items, section.ordinal(), NAV_WIDTH, i -> { section = Section.values()[i]; init(); }).position(contentX, contentY + 4)); int dividerX = contentX + NAV_WIDTH + NAV_GAP; draw(new Divider(1).dimensions(1, panelHeight).position(dividerX, panelY)); int x = dividerX + 1 + NAV_GAP + 4; int width = contentX + contentWidth - x; // Header draw(new Label(() -> section.title, 1.15f, () -> Theme.FONT.value, () -> Theme.FOREGROUND.value) .position(x, contentY + 2)); draw(new Label(() -> section.description, 0.66f, Theme::getBodyFont, Theme::getMutedFg) .position(x, contentY + 28)); ActionButton reset = new ActionButton(Textures.ICON_RESET, "Reset", true, this::reset); draw(reset.position(x + width - reset.width, contentY + 4)); // The section's settings Scroll scroll = new Scroll(0).noBackground(); int listWidth = scrollContentWidth(width); switch (section) { case GENERAL -> PropertyList.draw(scroll, getGeneralProperties(), Set.of(), listWidth, 0); case UI_STYLE -> drawUiStyle(scroll, listWidth); case ANIMATIONS -> drawAnimations(scroll, listWidth); } draw(scroll.dimensions(scrollWidth(width), contentHeight - LIST_Y).position(x, contentY + LIST_Y)); } private void drawUiStyle(Scroll scroll, int width) { Map properties = Theme.config.getProperties(); int y = PropertyList.drawSection(scroll, "Presets", 0) + 10; int cardWidth = (width - PRESET_GAP * (Theme.PRESETS.size() - 1)) / Theme.PRESETS.size(); for (int i = 0; i < Theme.PRESETS.size(); i++) { // Rebuild so the color inputs show the preset's colors scroll.draw(new ThemePresetCard(Theme.PRESETS.get(i), cardWidth, this::init) .position(i * (cardWidth + PRESET_GAP), y)); } y += ThemePresetCard.HEIGHT + SECTION_GAP; y = PropertyList.drawSection(scroll, "Colors", y) + 10; y = ColorGrid.draw(scroll, properties, width, y) + SECTION_GAP; y = PropertyList.drawSection(scroll, "Shape & type", y) + 4; PropertyList.draw(scroll, properties, getNames(properties, p -> p instanceof ColorProperty), width, y); } /** * Each menu's animation settings under the menu's name. */ private static void drawAnimations(Scroll scroll, int width) { int y = 0; for (AnimationConfig animation : AnimationConfig.ALL) { y = PropertyList.drawSection(scroll, animation.name, y) + 4; y = PropertyList.draw(scroll, animation.getProperties(), Set.of(), width, y) + SECTION_GAP; } } /** * The top-level settings. The namespaces have sections of their own. */ private static Map getGeneralProperties() { return Config.config.getProperties().entrySet().stream() .filter(e -> !(e.getValue() instanceof NamespaceProperty)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new)); } private static Set getNames(Map properties, Predicate filter) { return properties.entrySet().stream() .filter(e -> filter.test(e.getValue())) .map(Map.Entry::getKey) .collect(Collectors.toSet()); } /** * Puts the current section's settings back to their defaults. */ private void reset() { Map properties = switch (section) { case GENERAL -> getGeneralProperties(); case UI_STYLE -> Theme.config.getProperties(); case ANIMATIONS -> AnimationConfig.config.getProperties(); }; properties.values().forEach(Property::reset); init(); } }