[config] Clean up a bit
This commit is contained in:
parent
b655c6ab67
commit
a4aac90aed
|
@ -3,22 +3,22 @@ package io.gitlab.jfronny.libjf.config.api;
|
||||||
import io.gitlab.jfronny.gson.JsonElement;
|
import io.gitlab.jfronny.gson.JsonElement;
|
||||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||||
import io.gitlab.jfronny.libjf.config.impl.EntryInfoImpl;
|
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.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
//TODO isolate UI stuff from this
|
public interface EntryInfo<T> {
|
||||||
public interface EntryInfo<T> extends WidgetFactory {
|
static <T> @Nullable EntryInfo<T> fromField(Field field) {
|
||||||
static <T> EntryInfo<T> fromField(Field field) {
|
if (!field.isAnnotationPresent(Entry.class)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
EntryInfoImpl<T> info = new EntryInfoImpl<>();
|
EntryInfoImpl<T> info = new EntryInfoImpl<>();
|
||||||
info.field = field;
|
info.field = field;
|
||||||
if (field.isAnnotationPresent(Entry.class)) {
|
info.entry = field.getAnnotation(Entry.class);
|
||||||
info.entry = field.getAnnotation(Entry.class);
|
try {
|
||||||
try {
|
info.defaultValue = (T) field.get(null);
|
||||||
info.defaultValue = (T) field.get(null);
|
} catch (IllegalAccessException ignored) {}
|
||||||
} catch (IllegalAccessException ignored) {}
|
|
||||||
}
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,17 +69,17 @@ public interface EntryInfo<T> extends WidgetFactory {
|
||||||
void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException;
|
void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get metadata associated with this entry using the Entry class
|
* @return Get the width for this entry
|
||||||
* @return The annotation instance
|
|
||||||
*/
|
*/
|
||||||
Entry getEntryMeta();
|
int getWidth();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a widget factory for this entry
|
* @return Get the minimum value of this entry
|
||||||
* @param factory The factory to use
|
|
||||||
*/
|
*/
|
||||||
void setWidgetFactory(WidgetFactory factory);
|
double getMinValue();
|
||||||
|
|
||||||
record Widget(Runnable update, ClickableWidget widget) {
|
/**
|
||||||
}
|
* @return Get the maximum value for this entry
|
||||||
|
*/
|
||||||
|
double getMaxValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,11 @@ package io.gitlab.jfronny.libjf.config.api;
|
||||||
|
|
||||||
import net.minecraft.client.font.TextRenderer;
|
import net.minecraft.client.font.TextRenderer;
|
||||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||||
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||||
|
|
||||||
public interface WidgetFactory {
|
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) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ public abstract class ConfigInstanceAbstract implements ConfigInstance {
|
||||||
this.configClass = configClass;
|
this.configClass = configClass;
|
||||||
this.referencedConfigs = List.copyOf(meta.referencedConfigs);
|
this.referencedConfigs = List.copyOf(meta.referencedConfigs);
|
||||||
for (Field field : configClass.getFields()) {
|
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, () -> {
|
presets.put(CONFIG_PRESET_DEFAULT, () -> {
|
||||||
for (EntryInfo<?> entry : entries) {
|
for (EntryInfo<?> entry : entries) {
|
||||||
|
|
|
@ -98,17 +98,17 @@ public class EntryInfoImpl<T> implements EntryInfo<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry getEntryMeta() {
|
public int getWidth() {
|
||||||
return entry;
|
return entry.width();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setWidgetFactory(WidgetFactory factory) {
|
public double getMinValue() {
|
||||||
this.widget = factory;
|
return entry.min();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done) {
|
public double getMaxValue() {
|
||||||
return widget.build(screenWidth, textRenderer, done);
|
return entry.max();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,10 +133,9 @@ public class JfConfigCommand implements ModInitializer {
|
||||||
|
|
||||||
private <T> ArgumentType<?> getType(EntryInfo<T> info) {
|
private <T> ArgumentType<?> getType(EntryInfo<T> info) {
|
||||||
Class<T> type = info.getValueType();
|
Class<T> type = info.getValueType();
|
||||||
Entry e = info.getEntryMeta();
|
if (type == int.class || type == Integer.class) return IntegerArgumentType.integer((int) info.getMinValue(), (int) info.getMaxValue());
|
||||||
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) info.getMinValue(), (float) info.getMaxValue());
|
||||||
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(info.getMinValue(), info.getMaxValue());
|
||||||
else if (type == double.class || type == Double.class) return DoubleArgumentType.doubleArg(e.min(), e.max());
|
|
||||||
else if (type == String.class) return StringArgumentType.greedyString();
|
else if (type == String.class) return StringArgumentType.greedyString();
|
||||||
else if (type == boolean.class || type == Boolean.class) return BoolArgumentType.bool();
|
else if (type == boolean.class || type == Boolean.class) return BoolArgumentType.bool();
|
||||||
else return null;
|
else return null;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package io.gitlab.jfronny.libjf.config.impl.client.gui;
|
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.commons.tuple.Tuple;
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
|
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
|
||||||
import io.gitlab.jfronny.libjf.config.api.Entry;
|
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||||
import io.gitlab.jfronny.libjf.config.api.EntryInfo;
|
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 io.gitlab.jfronny.libjf.config.impl.EntryInfoImpl;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
|
@ -38,50 +40,41 @@ public class EntryInfoWidgetBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> WidgetState<T> initEntry(ConfigInstance config, EntryInfo<T> info, List<WidgetState<?>> knownStates) {
|
private static <T> WidgetState<T> initEntry(ConfigInstance config, EntryInfo<T> info, List<WidgetState<?>> knownStates) {
|
||||||
if (info.getEntryMeta() == null)
|
|
||||||
return null;
|
|
||||||
Class<T> type = info.getValueType();
|
Class<T> type = info.getValueType();
|
||||||
|
|
||||||
Entry meta = info.getEntryMeta();
|
|
||||||
|
|
||||||
WidgetState<T> state = new WidgetState<>();
|
WidgetState<T> state = new WidgetState<>();
|
||||||
state.entry = info;
|
WidgetFactory factory;
|
||||||
state.knownStates = knownStates;
|
|
||||||
|
|
||||||
if (type == int.class || type == Integer.class) textField(info, state, INTEGER_ONLY, Integer::parseInt, true, meta.min(), meta.max());
|
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) textField(info, state, DECIMAL_ONLY, Float::parseFloat, false, meta.min(), meta.max());
|
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) textField(info, state, DECIMAL_ONLY, Double::parseDouble, false, meta.min(), meta.max());
|
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) textField(info, state, null, String::length, true, Math.min(meta.min(),0), Math.max(meta.max(),1));
|
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) {
|
else if (type == boolean.class || type == Boolean.class) {
|
||||||
toggle(info, state,
|
factory = toggle(info, state,
|
||||||
value -> !(Boolean) value,
|
value -> !(Boolean) value,
|
||||||
value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED));
|
value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED));
|
||||||
} else if (type.isEnum()) {
|
} else if (type.isEnum()) {
|
||||||
List<T> values = Arrays.asList(info.getValueType().getEnumConstants());
|
List<T> values = Arrays.asList(info.getValueType().getEnumConstants());
|
||||||
toggle(info, state, value -> {
|
factory = toggle(info, state, value -> {
|
||||||
int index = values.indexOf(value) + 1;
|
int index = values.indexOf(value) + 1;
|
||||||
return values.get(index >= values.size() ? 0 : index);
|
return values.get(index >= values.size() ? 0 : index);
|
||||||
}, value -> new TranslatableText(config.getModId() + ".jfconfig.enum." + type.getSimpleName() + "." + state.cachedValue));
|
}, value -> new TranslatableText(config.getModId() + ".jfconfig.enum." + type.getSimpleName() + "." + state.cachedValue));
|
||||||
} else {
|
} else {
|
||||||
LibJf.LOGGER.error("Invalid entry type in " + info.getName() + ": " + type.getName());
|
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 {
|
Try.orThrow(() -> state.initialize(info, knownStates, factory));
|
||||||
state.updateCache(info.getValue());
|
|
||||||
} catch (IllegalAccessException ignored) {
|
|
||||||
}
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> void toggle(EntryInfo<T> info, WidgetState<T> state, Function<Object, Object> increment, Function<T, Text> valueTextifier) {
|
private static <T> WidgetFactory toggle(EntryInfo<T> info, WidgetState<T> state, Function<Object, Object> increment, Function<T, Text> valueTextifier) {
|
||||||
info.setWidgetFactory((screenWidth, textRenderer, done) -> {
|
return (screenWidth, textRenderer, done) -> {
|
||||||
ButtonWidget button = new ButtonWidget(screenWidth - 110, 0, info.getEntryMeta().width(), 20, valueTextifier.apply(state.cachedValue), btn -> {
|
ButtonWidget button = new ButtonWidget(screenWidth - 110, 0, info.getWidth(), 20, valueTextifier.apply(state.cachedValue), btn -> {
|
||||||
state.updateCache((T) increment.apply(state.cachedValue));
|
state.updateCache((T) increment.apply(state.cachedValue));
|
||||||
btn.setMessage(valueTextifier.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 min The minimum size of a valid value
|
||||||
* @param max The maximum size of a valid value
|
* @param max The maximum size of a valid value
|
||||||
*/
|
*/
|
||||||
private static <T> void textField(EntryInfo<T> info, WidgetState<T> state, Pattern pattern, Function<String, Number> sizeFetcher, boolean wholeNumber, double min, double max) {
|
private static <T> WidgetFactory textField(EntryInfo<T> info, WidgetState<T> state, Pattern pattern, Function<String, Number> sizeFetcher, boolean wholeNumber, double min, double max) {
|
||||||
boolean isNumber = pattern != null;
|
boolean isNumber = pattern != null;
|
||||||
info.setWidgetFactory((width, textRenderer, done) -> {
|
return (width, textRenderer, done) -> {
|
||||||
TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.getEntryMeta().width(), 20, null);
|
TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.getWidth(), 20, null);
|
||||||
|
|
||||||
widget.setText(state.tempValue);
|
widget.setText(state.tempValue);
|
||||||
widget.setTextPredicate(currentInput -> {
|
widget.setTextPredicate(currentInput -> {
|
||||||
|
@ -127,7 +120,7 @@ public class EntryInfoWidgetBuilder {
|
||||||
return true;
|
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);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package io.gitlab.jfronny.libjf.config.impl.client.gui;
|
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.commons.throwable.Try;
|
||||||
import io.gitlab.jfronny.libjf.config.api.ConfigHolder;
|
import io.gitlab.jfronny.libjf.config.api.ConfigHolder;
|
||||||
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
|
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
|
||||||
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 io.gitlab.jfronny.libjf.config.impl.client.gui.presets.PresetsScreen;
|
import io.gitlab.jfronny.libjf.config.impl.client.gui.presets.PresetsScreen;
|
||||||
import io.gitlab.jfronny.libjf.unsafe.SafeLog;
|
import io.gitlab.jfronny.libjf.unsafe.SafeLog;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
|
@ -69,12 +67,12 @@ public class TinyConfigScreen extends Screen {
|
||||||
}
|
}
|
||||||
for (WidgetState<?> info : widgets) {
|
for (WidgetState<?> info : widgets) {
|
||||||
TranslatableText name = new TranslatableText(translationPrefix + info.entry.getName());
|
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 -> {
|
ButtonWidget resetButton = new ButtonWidget(width - 155, 0, 40, 20, new TranslatableText("libjf-config-v0.reset"), (button -> {
|
||||||
info.reset();
|
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);
|
boolean visible = !Objects.equals(info.entry.getDefault(), info.cachedValue);
|
||||||
resetButton.active = visible;
|
resetButton.active = visible;
|
||||||
return visible;
|
return visible;
|
||||||
|
|
|
@ -2,18 +2,33 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui;
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.tuple.Tuple;
|
import io.gitlab.jfronny.commons.tuple.Tuple;
|
||||||
import io.gitlab.jfronny.libjf.config.api.EntryInfo;
|
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.client.gui.widget.TextFieldWidget;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WidgetState<T> { //TODO convert this to an API class as a representation of a widget (instead of EntryInfo.Widget/WidgetFactory)
|
public class WidgetState<T> {
|
||||||
|
public EntryInfo<T> entry;
|
||||||
|
public List<WidgetState<?>> knownStates;
|
||||||
|
|
||||||
public Tuple<TextFieldWidget, Text> error;
|
public Tuple<TextFieldWidget, Text> error;
|
||||||
public boolean inLimits = true;
|
public boolean inLimits = true;
|
||||||
public String tempValue;
|
public String tempValue;
|
||||||
public T cachedValue;
|
public T cachedValue;
|
||||||
public EntryInfo<T> entry;
|
public WidgetFactory factory;
|
||||||
public List<WidgetState<?>> knownStates;
|
|
||||||
|
public void initialize(EntryInfo<T> entry, List<WidgetState<?>> 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) {
|
public void updateCache(T newValue) {
|
||||||
cachedValue = newValue;
|
cachedValue = newValue;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user