diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java index 72e5256..922cdbf 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java @@ -3,22 +3,22 @@ package io.gitlab.jfronny.libjf.config.api; import io.gitlab.jfronny.gson.JsonElement; import io.gitlab.jfronny.gson.stream.JsonWriter; import io.gitlab.jfronny.libjf.config.impl.EntryInfoImpl; -import net.minecraft.client.gui.widget.ClickableWidget; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.lang.reflect.Field; -//TODO isolate UI stuff from this -public interface EntryInfo extends WidgetFactory { - static EntryInfo fromField(Field field) { +public interface EntryInfo { + static @Nullable EntryInfo fromField(Field field) { + if (!field.isAnnotationPresent(Entry.class)) { + return null; + } EntryInfoImpl info = new EntryInfoImpl<>(); info.field = field; - if (field.isAnnotationPresent(Entry.class)) { - info.entry = field.getAnnotation(Entry.class); - try { - info.defaultValue = (T) field.get(null); - } catch (IllegalAccessException ignored) {} - } + info.entry = field.getAnnotation(Entry.class); + try { + info.defaultValue = (T) field.get(null); + } catch (IllegalAccessException ignored) {} return info; } @@ -69,17 +69,17 @@ public interface EntryInfo extends WidgetFactory { void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException; /** - * Get metadata associated with this entry using the Entry class - * @return The annotation instance + * @return Get the width for this entry */ - Entry getEntryMeta(); + int getWidth(); /** - * Configure a widget factory for this entry - * @param factory The factory to use + * @return Get the minimum value of this entry */ - void setWidgetFactory(WidgetFactory factory); + double getMinValue(); - record Widget(Runnable update, ClickableWidget widget) { - } + /** + * @return Get the maximum value for this entry + */ + double getMaxValue(); } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java index 2a80144..d147e89 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java @@ -2,7 +2,11 @@ package io.gitlab.jfronny.libjf.config.api; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ClickableWidget; public interface WidgetFactory { - EntryInfo.Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done); + Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done); + + record Widget(Runnable updateControls, ClickableWidget control) { + } } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java index c7fa0fd..6207168 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java @@ -30,7 +30,8 @@ public abstract class ConfigInstanceAbstract implements ConfigInstance { this.configClass = configClass; this.referencedConfigs = List.copyOf(meta.referencedConfigs); for (Field field : configClass.getFields()) { - entries.add(EntryInfo.fromField(field)); + EntryInfo newInfo = EntryInfo.fromField(field); + if (newInfo != null) entries.add(newInfo); } presets.put(CONFIG_PRESET_DEFAULT, () -> { for (EntryInfo entry : entries) { diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java index b5f2779..06d0651 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java @@ -98,17 +98,17 @@ public class EntryInfoImpl implements EntryInfo { } @Override - public Entry getEntryMeta() { - return entry; + public int getWidth() { + return entry.width(); } @Override - public void setWidgetFactory(WidgetFactory factory) { - this.widget = factory; + public double getMinValue() { + return entry.min(); } @Override - public Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done) { - return widget.build(screenWidth, textRenderer, done); + public double getMaxValue() { + return entry.max(); } } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java index 042b4d3..2fa0042 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java @@ -133,10 +133,9 @@ public class JfConfigCommand implements ModInitializer { private ArgumentType getType(EntryInfo info) { Class type = info.getValueType(); - Entry e = info.getEntryMeta(); - if (type == int.class || type == Integer.class) return IntegerArgumentType.integer((int) e.min(), (int) e.max()); - else if (type == float.class || type == Float.class) return FloatArgumentType.floatArg((float) e.min(), (float) e.max()); - else if (type == double.class || type == Double.class) return DoubleArgumentType.doubleArg(e.min(), e.max()); + if (type == int.class || type == Integer.class) return IntegerArgumentType.integer((int) info.getMinValue(), (int) info.getMaxValue()); + else if (type == float.class || type == Float.class) return FloatArgumentType.floatArg((float) info.getMinValue(), (float) info.getMaxValue()); + else if (type == double.class || type == Double.class) return DoubleArgumentType.doubleArg(info.getMinValue(), info.getMaxValue()); else if (type == String.class) return StringArgumentType.greedyString(); else if (type == boolean.class || type == Boolean.class) return BoolArgumentType.bool(); else return null; diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java index 68d0cd4..75d1463 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java @@ -1,10 +1,12 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui; +import io.gitlab.jfronny.commons.throwable.Try; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.ConfigInstance; import io.gitlab.jfronny.libjf.config.api.Entry; import io.gitlab.jfronny.libjf.config.api.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.WidgetFactory; import io.gitlab.jfronny.libjf.config.impl.EntryInfoImpl; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -38,50 +40,41 @@ public class EntryInfoWidgetBuilder { } private static WidgetState initEntry(ConfigInstance config, EntryInfo info, List> knownStates) { - if (info.getEntryMeta() == null) - return null; Class type = info.getValueType(); - - Entry meta = info.getEntryMeta(); - WidgetState state = new WidgetState<>(); - state.entry = info; - state.knownStates = knownStates; + WidgetFactory factory; - if (type == int.class || type == Integer.class) textField(info, state, INTEGER_ONLY, Integer::parseInt, true, meta.min(), meta.max()); - else if (type == float.class || type == Float.class) textField(info, state, DECIMAL_ONLY, Float::parseFloat, false, meta.min(), meta.max()); - else if (type == double.class || type == Double.class) textField(info, state, DECIMAL_ONLY, Double::parseDouble, false, meta.min(), meta.max()); - else if (type == String.class) textField(info, state, null, String::length, true, Math.min(meta.min(),0), Math.max(meta.max(),1)); + if (type == int.class || type == Integer.class) factory = textField(info, state, INTEGER_ONLY, Integer::parseInt, true, info.getMinValue(), info.getMaxValue()); + else if (type == float.class || type == Float.class) factory = textField(info, state, DECIMAL_ONLY, Float::parseFloat, false, info.getMinValue(), info.getMaxValue()); + else if (type == double.class || type == Double.class) factory = textField(info, state, DECIMAL_ONLY, Double::parseDouble, false, info.getMinValue(), info.getMaxValue()); + else if (type == String.class) factory = textField(info, state, null, String::length, true, Math.min(info.getMinValue(),0), Math.max(info.getMaxValue(),1)); else if (type == boolean.class || type == Boolean.class) { - toggle(info, state, + factory = toggle(info, state, value -> !(Boolean) value, value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED)); } else if (type.isEnum()) { List values = Arrays.asList(info.getValueType().getEnumConstants()); - toggle(info, state, value -> { + factory = toggle(info, state, value -> { int index = values.indexOf(value) + 1; return values.get(index >= values.size() ? 0 : index); }, value -> new TranslatableText(config.getModId() + ".jfconfig.enum." + type.getSimpleName() + "." + state.cachedValue)); } else { LibJf.LOGGER.error("Invalid entry type in " + info.getName() + ": " + type.getName()); - info.setWidgetFactory(((screenWidth, textRenderer, done) -> new EntryInfo.Widget(() -> {}, new ButtonWidget(-10, 0, 0, 0, Text.of(""), null)))); + factory = ((screenWidth, textRenderer, done) -> new WidgetFactory.Widget(() -> {}, new ButtonWidget(-10, 0, 0, 0, Text.of(""), null))); } - try { - state.updateCache(info.getValue()); - } catch (IllegalAccessException ignored) { - } + Try.orThrow(() -> state.initialize(info, knownStates, factory)); return state; } - private static void toggle(EntryInfo info, WidgetState state, Function increment, Function valueTextifier) { - info.setWidgetFactory((screenWidth, textRenderer, done) -> { - ButtonWidget button = new ButtonWidget(screenWidth - 110, 0, info.getEntryMeta().width(), 20, valueTextifier.apply(state.cachedValue), btn -> { + private static WidgetFactory toggle(EntryInfo info, WidgetState state, Function increment, Function valueTextifier) { + return (screenWidth, textRenderer, done) -> { + ButtonWidget button = new ButtonWidget(screenWidth - 110, 0, info.getWidth(), 20, valueTextifier.apply(state.cachedValue), btn -> { state.updateCache((T) increment.apply(state.cachedValue)); btn.setMessage(valueTextifier.apply(state.cachedValue)); }); - return new EntryInfoImpl.Widget(() -> button.setMessage(valueTextifier.apply(state.cachedValue)), button); - }); + return new WidgetFactory.Widget(() -> button.setMessage(valueTextifier.apply(state.cachedValue)), button); + }; } /** @@ -93,10 +86,10 @@ public class EntryInfoWidgetBuilder { * @param min The minimum size of a valid value * @param max The maximum size of a valid value */ - private static void textField(EntryInfo info, WidgetState state, Pattern pattern, Function sizeFetcher, boolean wholeNumber, double min, double max) { + private static WidgetFactory textField(EntryInfo info, WidgetState state, Pattern pattern, Function sizeFetcher, boolean wholeNumber, double min, double max) { boolean isNumber = pattern != null; - info.setWidgetFactory((width, textRenderer, done) -> { - TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.getEntryMeta().width(), 20, null); + return (width, textRenderer, done) -> { + TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.getWidth(), 20, null); widget.setText(state.tempValue); widget.setTextPredicate(currentInput -> { @@ -127,7 +120,7 @@ public class EntryInfoWidgetBuilder { return true; }); - return new EntryInfoImpl.Widget(() -> widget.setText(state.cachedValue == null ? "" : state.cachedValue.toString()), widget); - }); + return new WidgetFactory.Widget(() -> widget.setText(state.cachedValue == null ? "" : state.cachedValue.toString()), widget); + }; } } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java index 4572baa..22431f5 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java @@ -1,11 +1,9 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui; -import io.gitlab.jfronny.commons.throwable.Coerce; import io.gitlab.jfronny.commons.throwable.Try; import io.gitlab.jfronny.libjf.config.api.ConfigHolder; import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.EntryInfo; -import io.gitlab.jfronny.libjf.config.impl.EntryInfoImpl; +import io.gitlab.jfronny.libjf.config.api.WidgetFactory; import io.gitlab.jfronny.libjf.config.impl.client.gui.presets.PresetsScreen; import io.gitlab.jfronny.libjf.unsafe.SafeLog; import net.fabricmc.api.EnvType; @@ -69,12 +67,12 @@ public class TinyConfigScreen extends Screen { } for (WidgetState info : widgets) { TranslatableText name = new TranslatableText(translationPrefix + info.entry.getName()); - EntryInfoImpl.Widget control = info.entry.build(width, textRenderer, done); + WidgetFactory.Widget control = info.factory.build(width, textRenderer, done); ButtonWidget resetButton = new ButtonWidget(width - 155, 0, 40, 20, new TranslatableText("libjf-config-v0.reset"), (button -> { info.reset(); - control.update().run(); + control.updateControls().run(); })); - this.list.addButton(control.widget(), resetButton, () -> { + this.list.addButton(control.control(), resetButton, () -> { boolean visible = !Objects.equals(info.entry.getDefault(), info.cachedValue); resetButton.active = visible; return visible; diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java index 7bd6a9e..77fa5b5 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java +++ b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java @@ -2,18 +2,33 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.config.api.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.WidgetFactory; +import io.gitlab.jfronny.libjf.unsafe.SafeLog; import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.text.Text; import java.util.List; -public class WidgetState { //TODO convert this to an API class as a representation of a widget (instead of EntryInfo.Widget/WidgetFactory) +public class WidgetState { + public EntryInfo entry; + public List> knownStates; + public Tuple error; public boolean inLimits = true; public String tempValue; public T cachedValue; - public EntryInfo entry; - public List> knownStates; + public WidgetFactory factory; + + public void initialize(EntryInfo entry, List> knownStates, WidgetFactory factory) { + this.entry = entry; + this.knownStates = knownStates; + this.factory = factory; + try { + updateCache(entry.getValue()); + } catch (IllegalAccessException e) { + SafeLog.error("Could not create initial widget state cache", e); + } + } public void updateCache(T newValue) { cachedValue = newValue;