diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java index dd999d7c..ec554502 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSync.java @@ -5,9 +5,9 @@ import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; import ru.bclib.BCLib; import ru.bclib.api.dataexchange.SyncFileHash; +import ru.bclib.config.ConfigUI; import ru.bclib.config.Configs; import ru.bclib.config.NamedPathConfig; -import ru.bclib.config.NamedPathConfig.ConfigToken.Bool; import ru.bclib.util.PathUtil; import java.io.File; @@ -32,12 +32,15 @@ public class AutoSync { @Environment(EnvType.CLIENT) public static class ClientConfig extends NamedPathConfig{ - public static final ConfigToken.Bool ENABLED = new Bool(true, "enabled", SYNC_CATEGORY); - public static final ConfigToken.Bool ACCEPT_CONFIGS = new Bool(true,"acceptConfigs", SYNC_CATEGORY); - public static final ConfigToken.Bool ACCEPT_FILES = new Bool(true,"acceptFiles", SYNC_CATEGORY); - public static final ConfigToken.Bool ACCEPT_MODS = new Bool(true,"acceptMods", SYNC_CATEGORY); - public static final ConfigToken.Bool SYNC_MOD_FOLDER = new Bool(false, "syncModFolder", SYNC_CATEGORY); - public static final ConfigToken.Bool DEBUG_HASHES = new Bool(true, "debugHashes", SYNC_CATEGORY); + public static final ConfigToken ENABLED = new ConfigToken(true, "enabled", SYNC_CATEGORY); + @ConfigUI(leftPadding =8) + public static final ConfigToken ACCEPT_CONFIGS = new DependendConfigToken(true,"acceptConfigs", SYNC_CATEGORY, (config)->config.get(ENABLED)); + @ConfigUI(leftPadding =8) + public static final ConfigToken ACCEPT_FILES = new DependendConfigToken(true,"acceptFiles", SYNC_CATEGORY, (config)->config.get(ENABLED)); + @ConfigUI(leftPadding =8) + public static final ConfigToken ACCEPT_MODS = new DependendConfigToken(true,"acceptMods", SYNC_CATEGORY, (config)->config.get(ENABLED)); + @ConfigUI(topPadding = 12) + public static final ConfigToken DEBUG_HASHES = new ConfigToken(true, "debugHashes", SYNC_CATEGORY); public ClientConfig(){ @@ -66,10 +69,10 @@ public class AutoSync { } public static class ServerConfig extends NamedPathConfig { - public static final ConfigToken.Bool ENABLED = new Bool(true, "enabled", SYNC_CATEGORY); - public static final ConfigToken.Bool OFFER_CONFIGS = new Bool(true,"offerConfigs", SYNC_CATEGORY); - public static final ConfigToken.Bool OFFER_FILES = new Bool(true,"offerFiles", SYNC_CATEGORY); - public static final ConfigToken.Bool OFFER_MODS = new Bool(true,"offerMods", SYNC_CATEGORY); + public static final ConfigToken ENABLED = new ConfigToken(true, "enabled", SYNC_CATEGORY); + public static final ConfigToken OFFER_CONFIGS = new ConfigToken(true,"offerConfigs", SYNC_CATEGORY); + public static final ConfigToken OFFER_FILES = new ConfigToken(true,"offerFiles", SYNC_CATEGORY); + public static final ConfigToken OFFER_MODS = new ConfigToken(true,"offerMods", SYNC_CATEGORY); public ServerConfig(){ diff --git a/src/main/java/ru/bclib/config/Config.java b/src/main/java/ru/bclib/config/Config.java index 0cedea0e..1eecd70c 100644 --- a/src/main/java/ru/bclib/config/Config.java +++ b/src/main/java/ru/bclib/config/Config.java @@ -12,6 +12,7 @@ import ru.bclib.config.ConfigKeeper.FloatEntry; import ru.bclib.config.ConfigKeeper.IntegerEntry; import ru.bclib.config.ConfigKeeper.RangeEntry; import ru.bclib.config.ConfigKeeper.StringEntry; +import ru.bclib.config.NamedPathConfig.ConfigToken; import java.io.File; import java.util.HashMap; @@ -209,6 +210,4 @@ public abstract class Config { } return false; } - - } diff --git a/src/main/java/ru/bclib/config/ConfigUI.java b/src/main/java/ru/bclib/config/ConfigUI.java new file mode 100644 index 00000000..b91975bc --- /dev/null +++ b/src/main/java/ru/bclib/config/ConfigUI.java @@ -0,0 +1,25 @@ +package ru.bclib.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface ConfigUI { + /** + * When {@code true}, this option will not generate UI-Elements. + */ + public boolean hide() default false; + + /** + * When a Widget is generated for this option, it will be indented by this Value + */ + public int leftPadding() default 0; + + /** + * When a Widget is generated for this option, it will be indented by this Value + */ + public int topPadding() default 0; +} diff --git a/src/main/java/ru/bclib/config/NamedPathConfig.java b/src/main/java/ru/bclib/config/NamedPathConfig.java index 7de65025..71ee456b 100644 --- a/src/main/java/ru/bclib/config/NamedPathConfig.java +++ b/src/main/java/ru/bclib/config/NamedPathConfig.java @@ -2,10 +2,6 @@ package ru.bclib.config; import net.minecraft.resources.ResourceLocation; import ru.bclib.BCLib; -import ru.bclib.config.NamedPathConfig.ConfigToken.Bool; -import ru.bclib.config.NamedPathConfig.ConfigToken.Float; -import ru.bclib.config.NamedPathConfig.ConfigToken.Int; -import ru.bclib.config.NamedPathConfig.ConfigToken.Str; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -14,45 +10,86 @@ import java.util.List; import java.util.function.Predicate; public class NamedPathConfig extends PathConfig{ - public abstract static class ConfigToken extends ConfigKey{ - public static class Int extends ConfigToken{ - public Int(int def, String entry, ResourceLocation path) { this(def, entry, path.getNamespace(), path.getPath());} - public Int(int def, String entry, String... path) { super(def, entry, path);} + public static class ConfigTokenDescription { + public final ConfigToken token; + public final String internalName; + public final Boolean hidden; + public final int leftPadding; + public final int topPadding; + + @SuppressWarnings("unchecked") + ConfigTokenDescription(Field fl) throws IllegalAccessException{ + token = (ConfigToken) fl.get(null); + internalName = fl.getName(); + + ConfigUI ui = fl.getAnnotation(ConfigUI.class); + if (ui!=null) { + this.hidden = ui.hide(); + leftPadding = ui.leftPadding(); + topPadding = ui.topPadding(); + } else { + this.hidden = false; + this.leftPadding = 0; + topPadding = 0; + } } - public static class Float extends ConfigToken{ - public Float(float def, String entry, ResourceLocation path) { this(def, entry, path.getNamespace(), path.getPath());} - public Float(float def, String entry, String... path) { super(def, entry, path); } + public String getPath(){ + StringBuilder path = new StringBuilder(); + for (String p : token.getPath()){ + path.append(".") + .append(p); + + } + path.append(".").append(token.getEntry()); + return path.toString(); + } + } + public static class DependendConfigToken extends ConfigToken{ + protected final Predicate dependenciesTrue; + + public DependendConfigToken(T defaultValue, String entry, ResourceLocation path, Predicate dependenciesTrue) { + this(defaultValue, entry, new String[]{path.getNamespace(), path.getPath()}, dependenciesTrue); } - public static class Bool extends ConfigToken{ - public Bool(boolean def, String entry, ResourceLocation path) { this(def, entry, path.getNamespace(), path.getPath());} - public Bool(boolean def, String entry, String... path) { super(def, entry, path); } + public DependendConfigToken(T defaultValue, String entry, String path, Predicate dependenciesTrue) { + super(defaultValue, entry, path); + this.dependenciesTrue = dependenciesTrue; } - public static class Str extends ConfigToken{ - public Str(String def, String entry, ResourceLocation path) { this(def, entry, path.getNamespace(), path.getPath());} - public Str(String def, String entry, String... path) { super(def, entry, path); } + public DependendConfigToken(T defaultValue, String entry, String[] path, Predicate dependenciesTrue) { + super(defaultValue, entry, path); + this.dependenciesTrue = dependenciesTrue; } - public static final Predicate ALWAYS_ENABLED = (config) -> true; - + public boolean dependenciesTrue(NamedPathConfig config){ + return dependenciesTrue.test(config); + } + } + + public static class ConfigToken extends ConfigKey{ public final T defaultValue; - protected final Predicate enabled; + public final Class type; - ConfigToken(T defaultValue, String entry, ResourceLocation path) { this(defaultValue, entry, path.getNamespace(), path.getPath()); } - ConfigToken(T defaultValue, String entry, String... path) { this(defaultValue, ALWAYS_ENABLED, entry, path); } - - ConfigToken(T defaultValue, String entry, ResourceLocation path, Predicate enabled) { this(defaultValue, enabled, entry, path.getNamespace(), path.getPath()); } - ConfigToken(T defaultValue, String entry, String path, Predicate enabled) { this(defaultValue, enabled, entry, path); } - ConfigToken(T defaultValue, String entry, String[] path, Predicate enabled) { this(defaultValue, enabled, entry, path); } - private ConfigToken(T defaultValue, Predicate enabled, String entry, String... path) { - super(entry, path); - this.enabled = enabled; - this.defaultValue = defaultValue + public ConfigToken(T defaultValue, String entry, ResourceLocation path) { + this(defaultValue, entry, path.getNamespace(), path.getPath()); } + @SuppressWarnings("unchecked") + public ConfigToken(T defaultValue, String entry, String... path) { + super(entry, path); + this.defaultValue = defaultValue; + + if (defaultValue == null){ + BCLib.LOGGER.error("[Internal Error] defaultValue should not be null (" +this.getEntry() +")"); + } + this.type = defaultValue!=null?(Class)defaultValue.getClass():(Class)Object.class; + } + + public boolean dependenciesTrue(NamedPathConfig config){ + return true; + } } public NamedPathConfig(String modID, String group, boolean autoSync, boolean diffContent) { @@ -70,13 +107,13 @@ public class NamedPathConfig extends PathConfig{ onInit(); } - public List getAllOptions(){ - List res = new LinkedList<>(); + public List> getAllOptions(){ + List> res = new LinkedList<>(); for (Field fl : this.getClass().getDeclaredFields()){ int modifiers = fl.getModifiers(); if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && ConfigToken.class.isAssignableFrom(fl.getType())) { try { - res.add((ConfigToken) fl.get(null)); + res.add(new ConfigTokenDescription<>(fl)); } catch (IllegalAccessException e) { BCLib.LOGGER.error("Could not access " + fl); @@ -87,55 +124,90 @@ public class NamedPathConfig extends PathConfig{ } protected void onInit(){ - getAllOptions().forEach(e -> get(e)); + getAllOptions().forEach(e -> get(e.token)); this.saveChanges(); } - private void set(ConfigToken what, Object value) { - if (what instanceof Bool) set(what, (boolean)value); - else if (what instanceof Int) set(what, (int)value); - else if (what instanceof Float) set(what, (float)value); - else if (what instanceof Str) set(what, (String)value); - else BCLib.LOGGER.error("Accessing " + what + " as general type is not supported."); + /** + * The value without any check of {@link DependendConfigToken} + *

+ * In most cases you probably want to use {@link #get(ConfigToken)}, we use this Method if we + * present the actual value of the Settings from the Config File without any additional processing. + * + * @param what The Option you want to get + * @param The Type of the Option + * @return The Value of the Option (without checking the {@link DependendConfigToken)): + */ + public T getRaw(ConfigToken what){ + return _get(what, true); } - private Object get(ConfigToken what){ - if (what instanceof Bool) return get((Bool)what); - else if (what instanceof Int) return get((Int)what); - else if (what instanceof Float) return get((Float)what); - else if (what instanceof Str) return get((Str)what); - else return null; + /** + * The value of an Option + * @param what he Option you want to get + * @param The Type of the Option + * @return The Value of the Option. If this option is a {@link DependendConfigToken}, the returned value + * may not be the value from the config File. For Example, on a {@link Boolean}-Type the result is always false + * if {@link DependendConfigToken#dependenciesTrue} returns {@code false}. + */ + public T get(ConfigToken what){ + return _get(what, false); } - public void set(ConfigToken.Int what, int value) { - this.setInt(what, value); + @SuppressWarnings("unchecked") + private T _get(ConfigToken what, boolean raw){ + //TODO: Check if we can make config fully Generic to avoid runtime type checks... + if (Boolean.class.isAssignableFrom(what.type)){ + return (T)_getBoolean((ConfigToken)what, raw); + } + if (Integer.class.isAssignableFrom(what.type)){ + return (T)_getInt((ConfigToken)what); + } + if (Float.class.isAssignableFrom(what.type)){ + return (T)_getFloat((ConfigToken)what); + } + if (String.class.isAssignableFrom(what.type)){ + return (T)_getString((ConfigToken)what); + } + return this._get(what); } - public int get(ConfigToken.Int what){ - return this.getInt(what, what.defaultValue); + private T _get(ConfigToken what){ + BCLib.LOGGER.error(what + " has unsupported Type."); + return what.defaultValue; } - public void set(ConfigToken.Bool what, boolean value) { + public void set(ConfigToken what, boolean value) { this.setBoolean(what, value); } - - public boolean get(ConfigToken.Bool what){ + private Boolean _getBoolean(ConfigToken what, boolean raw){ + if (!raw && !what.dependenciesTrue(this)){ + return false; + } + return this.getBoolean(what, what.defaultValue); } - public void set(ConfigToken.Str what, String value) { - this.setString(what, value); + public void set(ConfigToken what, int value) { + this.setInt(what, value); + } + private Integer _getInt(ConfigToken what){ + return this.getInt(what, what.defaultValue); } - public String get(ConfigToken.Str what){ + public void set(ConfigToken what, float value) { + this.setFloat(what, value); + } + private Float _getFloat(ConfigToken what){ + return this.getFloat(what, what.defaultValue); + } + + public void set(ConfigToken what, String value) { + this.setString(what, value); + } + private String _getString(ConfigToken what){ return this.getString(what, what.defaultValue); } - public void set(ConfigToken.Float what, float value) { - this.setFloat(what, value); - } - public float get(ConfigToken.Float what){ - return this.getFloat(what, what.defaultValue); - } } diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java b/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java index ec1e93f9..dc6d3c23 100644 --- a/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java +++ b/src/main/java/ru/bclib/gui/gridlayout/GridCheckboxCell.java @@ -27,8 +27,11 @@ class SignalingCheckBox extends Checkbox{ } @Environment(EnvType.CLIENT) -public class GridCheckboxCell extends GridCell{ +public class GridCheckboxCell extends GridCell implements GridWidgetWithEnabledState{ private boolean checked; + private Checkbox lastCheckbox; + private boolean enabled; + private final float alpha; GridCheckboxCell(Component text, boolean checked, float alpha, double width, GridValueType widthType, double height) { this(text, checked, alpha, width, widthType, height, null); @@ -36,7 +39,9 @@ public class GridCheckboxCell extends GridCell{ GridCheckboxCell(Component text, boolean checked, float alpha, double width, GridValueType widthType, double height, Consumer onChange) { super(width, height, widthType, null, null); - + lastCheckbox = null; + enabled = true; + this.alpha = alpha; this.componentPlacer = (transform) -> { Checkbox cb = new SignalingCheckBox(transform.left, transform.top, transform.width, transform.height, text, @@ -47,11 +52,26 @@ public class GridCheckboxCell extends GridCell{ } ); cb.setAlpha(alpha); + lastCheckbox = cb; + setEnabled(enabled); return cb; }; + } public boolean isChecked(){ return checked; } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + if (lastCheckbox!=null){ + lastCheckbox.active = enabled; + lastCheckbox.setAlpha(enabled?alpha:(alpha *0.5f)); + } + this.enabled = enabled; + } } diff --git a/src/main/java/ru/bclib/gui/gridlayout/GridWidgetWithEnabledState.java b/src/main/java/ru/bclib/gui/gridlayout/GridWidgetWithEnabledState.java new file mode 100644 index 00000000..caa9f150 --- /dev/null +++ b/src/main/java/ru/bclib/gui/gridlayout/GridWidgetWithEnabledState.java @@ -0,0 +1,6 @@ +package ru.bclib.gui.gridlayout; + +public interface GridWidgetWithEnabledState { + public boolean isEnabled(); + public void setEnabled(boolean enabled); +} diff --git a/src/main/java/ru/bclib/gui/modmenu/MainScreen.java b/src/main/java/ru/bclib/gui/modmenu/MainScreen.java index bec9e5de..29a41d7b 100644 --- a/src/main/java/ru/bclib/gui/modmenu/MainScreen.java +++ b/src/main/java/ru/bclib/gui/modmenu/MainScreen.java @@ -6,11 +6,17 @@ import net.minecraft.network.chat.TranslatableComponent; import org.jetbrains.annotations.Nullable; import ru.bclib.config.Configs; import ru.bclib.config.NamedPathConfig; -import ru.bclib.config.NamedPathConfig.ConfigToken; -import ru.bclib.config.NamedPathConfig.ConfigToken.Bool; +import ru.bclib.config.NamedPathConfig.ConfigTokenDescription; +import ru.bclib.config.NamedPathConfig.DependendConfigToken; +import ru.bclib.gui.gridlayout.GridCheckboxCell; import ru.bclib.gui.gridlayout.GridColumn; import ru.bclib.gui.gridlayout.GridRow; import ru.bclib.gui.gridlayout.GridScreen; +import ru.bclib.gui.gridlayout.GridWidgetWithEnabledState; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; public class MainScreen extends GridScreen{ @@ -18,28 +24,42 @@ public class MainScreen extends GridScreen{ super(parent, new TranslatableComponent("title.bclib.modmenu.main")); } - protected TranslatableComponent getComponent(NamedPathConfig config, ConfigToken.Bool token, String type){ - StringBuilder path = new StringBuilder(); - for (String p : token.getPath()){ - path.append(".") - .append(p); - - } - path.append(".").append(token.getEntry()); - return new TranslatableComponent(type + ".config." + config.configID + path ); + protected TranslatableComponent getComponent(NamedPathConfig config, ConfigTokenDescription option, String type){ + return new TranslatableComponent(type + ".config." + config.configID + option.getPath() ); } - protected void addRow(GridColumn grid, NamedPathConfig config, ConfigToken token){ - if (token instanceof Bool){ - addRow(grid, config, (ConfigToken.Bool)token); + Map> dependentWidgets = new HashMap<>(); + protected void updateEnabledState(){ + dependentWidgets.forEach((cb, supl)->cb.setEnabled(supl.get())); + } + + @SuppressWarnings("unchecked") + protected void addRow(GridColumn grid, NamedPathConfig config, ConfigTokenDescription option){ + if (Boolean.class.isAssignableFrom(option.token.type)) { + addCheckbox(grid, config, (ConfigTokenDescription)option); } grid.addSpacerRow(2); } - protected void addRow(GridColumn grid, NamedPathConfig config, ConfigToken.Bool token){ + + protected void addCheckbox(GridColumn grid, NamedPathConfig config, ConfigTokenDescription option){ + if (option.topPadding>0){ + grid.addSpacerRow(option.topPadding); + } GridRow row = grid.addRow(); - row.addCheckbox(getComponent(config, token, "title"), config.get(token), font, (state)-> config.set(token, state)); + if (option.leftPadding>0){ + row.addSpacer(option.leftPadding); + } + GridCheckboxCell cb = row.addCheckbox(getComponent(config, option, "title"), config.getRaw(option.token), font, (state)-> { + config.set(option.token, state); + updateEnabledState(); + }); + + if (option.token instanceof DependendConfigToken) { + dependentWidgets.put(cb, ()->option.token.dependenciesTrue(config)); + cb.setEnabled(option.token.dependenciesTrue(config)); + } } @Override @@ -51,7 +71,7 @@ public class MainScreen extends GridScreen{ protected void initLayout() { final int BUTTON_HEIGHT = 20; - Configs.CLIENT_CONFIG.getAllOptions().forEach(o -> addRow(grid, Configs.CLIENT_CONFIG, o)); + Configs.CLIENT_CONFIG.getAllOptions().stream().filter(o -> !o.hidden).forEach(o -> addRow(grid, Configs.CLIENT_CONFIG, o)); grid.addSpacerRow(15); GridRow row = grid.addRow(); diff --git a/src/main/resources/assets/bclib/lang/en_us.json b/src/main/resources/assets/bclib/lang/en_us.json index 466bc574..36e099a6 100644 --- a/src/main/resources/assets/bclib/lang/en_us.json +++ b/src/main/resources/assets/bclib/lang/en_us.json @@ -23,5 +23,5 @@ "title.config.bclib.client.auto_sync.acceptFiles": "Accept incoming Files", "title.config.bclib.client.auto_sync.acceptMods": "Accept incoming Mods", "title.config.bclib.client.auto_sync.syncModFolder": "Sync entire Mod-Folder from Server", - "title.config.bclib.client.auto_sync.debugHashes": "Print Debug-Hashes to Log" + "title.config.bclib.client.auto_sync.debugHashes": "Print Auto-Sync Debug-Hashes to Log" } \ No newline at end of file