126 lines
6.2 KiB
Java
126 lines
6.2 KiB
Java
package io.gitlab.jfronny.libjf.config.impl.client.screen;
|
|
|
|
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.impl.EntryInfo;
|
|
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
|
import net.minecraft.text.LiteralText;
|
|
import net.minecraft.text.Text;
|
|
import net.minecraft.text.TranslatableText;
|
|
import net.minecraft.util.Formatting;
|
|
|
|
import java.util.AbstractMap;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.regex.Pattern;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class EntryInfoWidgetBuilder {
|
|
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
|
|
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
|
|
|
|
public static void initConfig(ConfigInstance config) {
|
|
for (EntryInfo info : config.getEntries()) {
|
|
if (info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))
|
|
try {
|
|
initEntry(config, info);
|
|
} catch (Exception ignored) {
|
|
}
|
|
}
|
|
for (ConfigInstance value : config.getCategories().values()) {
|
|
initConfig(value);
|
|
}
|
|
}
|
|
|
|
private static void initEntry(ConfigInstance config, EntryInfo info) {
|
|
if (!(info.field.isAnnotationPresent(io.gitlab.jfronny.libjf.config.api.Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))) return;
|
|
Class<?> type = info.field.getType();
|
|
info.width = info.entry != null ? info.entry.width() : 0;
|
|
|
|
if (info.entry == null) return;
|
|
|
|
if (type == int.class || type == Integer.class) textField(config, info, INTEGER_ONLY, Integer::parseInt, true, info.entry.min(), info.entry.max());
|
|
else if (type == float.class || type == Float.class) textField(config, info, DECIMAL_ONLY, Float::parseFloat, false, info.entry.min(), info.entry.max());
|
|
else if (type == double.class || type == Double.class) textField(config, info, DECIMAL_ONLY, Double::parseDouble, false, info.entry.min(), info.entry.max());
|
|
else if (type == String.class) textField(config, info, null, String::length, true, Math.min(info.entry.min(),0), Math.max(info.entry.max(),1));
|
|
else if (type == boolean.class || type == Boolean.class) {
|
|
Function<Object, Text> func = value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED);
|
|
toggle(info, button -> {
|
|
info.value = !(Boolean) info.value;
|
|
button.setMessage(func.apply(info.value));
|
|
}, func);
|
|
} else if (type.isEnum()) {
|
|
List<?> values = Arrays.asList(info.field.getType().getEnumConstants());
|
|
Function<Object,Text> func = value -> new TranslatableText(config.getModId() + ".jfconfig.enum." + type.getSimpleName() + "." + info.value.toString());
|
|
toggle(info, button -> {
|
|
int index = values.indexOf(info.value) + 1;
|
|
info.value = values.get(index >= values.size() ? 0 : index);
|
|
button.setMessage(func.apply(info.value));
|
|
}, func);
|
|
} else LibJf.LOGGER.error("Invalid entry type in " + info.field.getName() + ": " + type.getName());
|
|
|
|
try {
|
|
info.value = info.field.get(null);
|
|
info.tempValue = info.value.toString();
|
|
} catch (IllegalAccessException ignored) {
|
|
}
|
|
}
|
|
|
|
private static void toggle(EntryInfo info, ButtonWidget.PressAction pressAction, Function<Object, Text> valueTextifier) {
|
|
info.widget = (width, textRenderer, done) -> new ButtonWidget(width - 110, 0, info.width, 20, valueTextifier.apply(info.value), pressAction);
|
|
}
|
|
|
|
/**
|
|
* @param config The config this entry is a part of
|
|
* @param info The entry to generate a widget for
|
|
* @param pattern The pattern a valid value must abide to
|
|
* @param sizeFetcher A function to get a number for size constraints
|
|
* @param wholeNumber Whether size constraints are whole numbers
|
|
* @param min The minimum size of a valid value
|
|
* @param max The maximum size of a valid value
|
|
*/
|
|
private static void textField(ConfigInstance config, EntryInfo info, Pattern pattern, Function<String, Number> sizeFetcher, boolean wholeNumber, double min, double max) {
|
|
boolean isNumber = pattern != null;
|
|
info.widget = (width, textRenderer, done) -> {
|
|
TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.width, 20, null);
|
|
|
|
widget.setText(info.tempValue);
|
|
Predicate<String> processor = s -> {
|
|
s = s.trim();
|
|
if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches())) return false;
|
|
|
|
Number value = 0;
|
|
boolean inLimits = false;
|
|
info.error = null;
|
|
if (!(isNumber && s.isEmpty()) && !s.equals("-") && !s.equals(".")) {
|
|
value = sizeFetcher.apply(s);
|
|
inLimits = value.doubleValue() >= min && value.doubleValue() <= max;
|
|
info.error = inLimits? null : new AbstractMap.SimpleEntry<>(widget, new LiteralText(value.doubleValue() < min ?
|
|
"§cMinimum " + (isNumber? "value" : "length") + (wholeNumber ? " is " + (int) min : " is " + min) :
|
|
"§cMaximum " + (isNumber? "value" : "length") + (wholeNumber ? " is " + (int) max : " is " + max)));
|
|
}
|
|
|
|
info.tempValue = s;
|
|
widget.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
|
|
info.inLimits = inLimits;
|
|
done.active = config.getEntries().stream().allMatch(e -> e.inLimits);
|
|
|
|
if (inLimits)
|
|
info.value = isNumber? value : s;
|
|
|
|
return true;
|
|
};
|
|
widget.setTextPredicate(processor);
|
|
|
|
return widget;
|
|
};
|
|
}
|
|
}
|