mirror of
https://github.com/TeamMidnightDust/MidnightLib.git
synced 2025-12-16 09:15:10 +01:00
clean: extract inner classes
This commit is contained in:
@@ -15,7 +15,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static eu.midnightdust.lib.config.MidnightConfig.MidnightConfigListWidget;
|
import eu.midnightdust.lib.config.MidnightConfigListWidget;
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
public class MidnightConfigOverviewScreen extends Screen {
|
public class MidnightConfigOverviewScreen extends Screen {
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package eu.midnightdust.lib.config;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.font.TextRenderer;
|
||||||
|
import net.minecraft.client.gui.Click;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.Element;
|
||||||
|
import net.minecraft.client.gui.Selectable;
|
||||||
|
import net.minecraft.client.gui.screen.ConfirmLinkScreen;
|
||||||
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||||
|
import net.minecraft.client.gui.widget.ElementListWidget;
|
||||||
|
import net.minecraft.client.gui.widget.MultilineTextWidget;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ButtonEntry extends ElementListWidget.Entry<ButtonEntry> {
|
||||||
|
private static final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
|
||||||
|
public final Text text;
|
||||||
|
public final List<ClickableWidget> buttons;
|
||||||
|
public final EntryInfo info;
|
||||||
|
public boolean centered = false;
|
||||||
|
public MultilineTextWidget title;
|
||||||
|
|
||||||
|
public ButtonEntry(List<ClickableWidget> buttons, Text text, EntryInfo info) {
|
||||||
|
this.buttons = buttons;
|
||||||
|
this.text = text;
|
||||||
|
this.info = info;
|
||||||
|
if (info != null && info.comment != null)
|
||||||
|
this.centered = info.comment.centered();
|
||||||
|
int scaledWidth = MinecraftClient.getInstance().getWindow().getScaledWidth();
|
||||||
|
|
||||||
|
if (text != null && (!text.getString().contains("spacer") || !buttons.isEmpty())) {
|
||||||
|
title = new MultilineTextWidget((centered) ? (scaledWidth / 2 - (textRenderer.getWidth(text) / 2)) : 12, 0, Text.of(text), textRenderer);
|
||||||
|
title.setCentered(centered);
|
||||||
|
if (info != null)
|
||||||
|
title.setTooltip(info.getTooltip(false));
|
||||||
|
title.setMaxWidth(!buttons.isEmpty() ? buttons.get(buttons.size() > 2 ? buttons.size() - 1 : 0).getX() - 16 : scaledWidth - 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(DrawContext context, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||||
|
buttons.forEach(b -> {
|
||||||
|
b.setY(this.getY());
|
||||||
|
b.render(context, mouseX, mouseY, tickDelta);
|
||||||
|
});
|
||||||
|
if (title != null) {
|
||||||
|
title.setY(this.getY() + 5);
|
||||||
|
title.render(context, mouseX, mouseY, tickDelta);
|
||||||
|
|
||||||
|
if (info.entry != null && !this.buttons.isEmpty() && this.buttons.getFirst() instanceof ClickableWidget widget) {
|
||||||
|
int idMode = this.info.entry.idMode();
|
||||||
|
if (idMode != -1) context.drawItem(idMode == 0 ?
|
||||||
|
Registries.ITEM.get(Identifier.tryParse(this.info.tempValue)).getDefaultStack()
|
||||||
|
: Registries.BLOCK.get(Identifier.tryParse(this.info.tempValue)).asItem().getDefaultStack(),
|
||||||
|
widget.getX() + widget.getWidth() - 18, this.getY() + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseClicked(Click click, boolean doubled) {
|
||||||
|
if (this.info != null && this.info.comment != null && !this.info.comment.url().isBlank())
|
||||||
|
ConfirmLinkScreen.open(MinecraftClient.getInstance().currentScreen, this.info.comment.url(), true);
|
||||||
|
return super.mouseClicked(click, doubled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<? extends Element> children() {
|
||||||
|
return Lists.newArrayList(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<? extends Selectable> selectableChildren() {
|
||||||
|
return Lists.newArrayList(buttons);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
common/src/main/java/eu/midnightdust/lib/config/EntryInfo.java
Normal file
102
common/src/main/java/eu/midnightdust/lib/config/EntryInfo.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package eu.midnightdust.lib.config;
|
||||||
|
|
||||||
|
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||||
|
import net.minecraft.client.gui.tab.Tab;
|
||||||
|
import net.minecraft.client.gui.tooltip.Tooltip;
|
||||||
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||||
|
import net.minecraft.client.resource.language.I18n;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EntryInfo {
|
||||||
|
public MidnightConfig.Entry entry;
|
||||||
|
public MidnightConfig.Comment comment;
|
||||||
|
public MidnightConfig.Condition[] conditions;
|
||||||
|
public final Field field;
|
||||||
|
public final Class<?> dataType;
|
||||||
|
public final String modid, fieldName;
|
||||||
|
int listIndex;
|
||||||
|
Object defaultValue, value, function;
|
||||||
|
String tempValue; // The value visible in the config screen
|
||||||
|
boolean inLimits = true;
|
||||||
|
Text name, error;
|
||||||
|
ClickableWidget actionButton; // color picker button / explorer button
|
||||||
|
Tab tab;
|
||||||
|
boolean conditionsMet = true;
|
||||||
|
|
||||||
|
public EntryInfo(Field field, String modid) {
|
||||||
|
this.field = field;
|
||||||
|
this.modid = modid;
|
||||||
|
if (field != null) {
|
||||||
|
this.fieldName = field.getName();
|
||||||
|
this.dataType = MidnightConfig.getUnderlyingType(field);
|
||||||
|
this.entry = field.getAnnotation(MidnightConfig.Entry.class);
|
||||||
|
this.comment = field.getAnnotation(MidnightConfig.Comment.class);
|
||||||
|
this.conditions = field.getAnnotationsByType(MidnightConfig.Condition.class);
|
||||||
|
} else {
|
||||||
|
this.fieldName = "";
|
||||||
|
this.dataType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry != null && !entry.name().isEmpty())
|
||||||
|
this.name = Text.translatable(entry.name());
|
||||||
|
else if (comment != null && !comment.name().isEmpty())
|
||||||
|
this.name = Text.translatable(comment.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Object value) {
|
||||||
|
if (this.field.getType() != List.class) {
|
||||||
|
this.value = value;
|
||||||
|
this.tempValue = value.toString();
|
||||||
|
} else {
|
||||||
|
writeList(this.listIndex, value);
|
||||||
|
this.tempValue = toTemporaryValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toTemporaryValue() {
|
||||||
|
if (this.field.getType() != List.class) return this.value.toString();
|
||||||
|
else try {
|
||||||
|
return ((List<?>) this.value).get(this.listIndex).toString();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFieldValue() {
|
||||||
|
try {
|
||||||
|
if (this.field.get(null) != value) MidnightConfig.entries.values().forEach(EntryInfo::updateConditions);
|
||||||
|
this.field.set(null, this.value);
|
||||||
|
} catch (IllegalAccessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantValue") //pertains to requiredModLoaded
|
||||||
|
public void updateConditions() {
|
||||||
|
boolean prevConditionState = this.conditionsMet;
|
||||||
|
if (this.conditions.length > 0) this.conditionsMet = true; // reset conditions
|
||||||
|
for (MidnightConfig.Condition condition : this.conditions) {
|
||||||
|
if (!condition.requiredModId().isEmpty() && !PlatformFunctions.isModLoaded(condition.requiredModId()))
|
||||||
|
this.conditionsMet = false;
|
||||||
|
String requiredOption = condition.requiredOption().contains(":") ? condition.requiredOption() : (this.modid + ":" + condition.requiredOption());
|
||||||
|
if (MidnightConfig.entries.get(requiredOption) instanceof EntryInfo info)
|
||||||
|
this.conditionsMet &= List.of(condition.requiredValue()).contains(info.tempValue);
|
||||||
|
if (!this.conditionsMet) break;
|
||||||
|
}
|
||||||
|
if (prevConditionState != this.conditionsMet) MidnightConfig.configInstances.get(modid).reloadScreen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void writeList(int index, T value) {
|
||||||
|
var list = (List<T>) this.value;
|
||||||
|
if (index >= list.size())
|
||||||
|
list.add(value);
|
||||||
|
else list.set(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tooltip getTooltip(boolean isButton) {
|
||||||
|
String key = this.modid + ".midnightconfig." + this.fieldName + (!isButton ? ".label" : "") + ".tooltip";
|
||||||
|
return Tooltip.of(isButton && this.error != null ? this.error : I18n.hasTranslation(key) ? Text.translatable(key) : Text.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +1,19 @@
|
|||||||
package eu.midnightdust.lib.config;
|
package eu.midnightdust.lib.config;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.google.gson.stream.*;
|
import com.google.gson.stream.*;
|
||||||
import eu.midnightdust.lib.util.PlatformFunctions;
|
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.font.TextRenderer;
|
|
||||||
import net.minecraft.client.gl.RenderPipelines;
|
|
||||||
import net.minecraft.client.gui.Click;
|
|
||||||
import net.minecraft.client.gui.DrawContext;
|
|
||||||
import net.minecraft.client.gui.Element;
|
|
||||||
import net.minecraft.client.gui.Selectable;
|
|
||||||
import net.minecraft.client.gui.screen.ConfirmLinkScreen;
|
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
import net.minecraft.client.gui.tab.GridScreenTab;
|
|
||||||
import net.minecraft.client.gui.tab.Tab;
|
|
||||||
import net.minecraft.client.gui.tab.TabManager;
|
|
||||||
import net.minecraft.client.gui.tooltip.Tooltip;
|
|
||||||
import net.minecraft.client.gui.widget.*;
|
import net.minecraft.client.gui.widget.*;
|
||||||
import net.minecraft.client.input.KeyInput;
|
|
||||||
import net.minecraft.client.resource.language.I18n;
|
import net.minecraft.client.resource.language.I18n;
|
||||||
import net.minecraft.registry.Registries;
|
|
||||||
import net.minecraft.screen.ScreenTexts;
|
|
||||||
import net.minecraft.text.Style;
|
import net.minecraft.text.Style;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.text.TranslatableTextContent;
|
|
||||||
import net.minecraft.util.*;
|
import net.minecraft.util.*;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
@@ -56,118 +38,36 @@ public abstract class MidnightConfig {
|
|||||||
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
|
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
|
||||||
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?(\\d+\\.?\\d*|\\d*\\.?\\d+|\\.)");
|
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?(\\d+\\.?\\d*|\\d*\\.?\\d+|\\.)");
|
||||||
private static final Pattern HEXADECIMAL_ONLY = Pattern.compile("(-?[#0-9a-fA-F]*)");
|
private static final Pattern HEXADECIMAL_ONLY = Pattern.compile("(-?[#0-9a-fA-F]*)");
|
||||||
|
|
||||||
private static final LinkedHashMap<String, EntryInfo> entries = new LinkedHashMap<>(); // modid:fieldName -> EntryInfo
|
|
||||||
private boolean reloadScreen = false;
|
|
||||||
|
|
||||||
public static class EntryInfo {
|
|
||||||
public Entry entry;
|
|
||||||
public Comment comment;
|
|
||||||
public Condition[] conditions;
|
|
||||||
public final Field field;
|
|
||||||
public final Class<?> dataType;
|
|
||||||
public final String modid, fieldName;
|
|
||||||
int listIndex;
|
|
||||||
Object defaultValue, value, function;
|
|
||||||
String tempValue; // The value visible in the config screen
|
|
||||||
boolean inLimits = true;
|
|
||||||
Text name, error;
|
|
||||||
ClickableWidget actionButton; // color picker button / explorer button
|
|
||||||
Tab tab;
|
|
||||||
boolean conditionsMet = true;
|
|
||||||
|
|
||||||
public EntryInfo(Field field, String modid) {
|
|
||||||
this.field = field; this.modid = modid;
|
|
||||||
if (field != null) {
|
|
||||||
this.fieldName = field.getName();
|
|
||||||
this.dataType = getUnderlyingType(field);
|
|
||||||
this.entry = field.getAnnotation(Entry.class);
|
|
||||||
this.comment = field.getAnnotation(Comment.class);
|
|
||||||
this.conditions = field.getAnnotationsByType(Condition.class);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.fieldName = "";
|
|
||||||
this.dataType = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry != null && !entry.name().isEmpty())
|
|
||||||
this.name = Text.translatable(entry.name());
|
|
||||||
else if (comment != null && !comment.name().isEmpty())
|
|
||||||
this.name = Text.translatable(comment.name());
|
|
||||||
}
|
|
||||||
public void setValue(Object value) {
|
|
||||||
if (this.field.getType() != List.class) { this.value = value;
|
|
||||||
this.tempValue = value.toString();
|
|
||||||
} else { writeList(this.listIndex, value);
|
|
||||||
this.tempValue = toTemporaryValue(); }
|
|
||||||
}
|
|
||||||
public String toTemporaryValue() {
|
|
||||||
if (this.field.getType() != List.class) return this.value.toString();
|
|
||||||
else try { return ((List<?>) this.value).get(this.listIndex).toString(); }
|
|
||||||
catch (Exception ignored) {return "";}
|
|
||||||
}
|
|
||||||
public void updateFieldValue() {
|
|
||||||
try {
|
|
||||||
if (this.field.get(null) != value) entries.values().forEach(EntryInfo::updateConditions);
|
|
||||||
this.field.set(null, this.value);
|
|
||||||
} catch (IllegalAccessException ignored) {}
|
|
||||||
}
|
|
||||||
@SuppressWarnings("ConstantValue") //pertains to requiredModLoaded
|
|
||||||
public void updateConditions() {
|
|
||||||
boolean prevConditionState = this.conditionsMet;
|
|
||||||
if (this.conditions.length > 0) this.conditionsMet = true; // reset conditions
|
|
||||||
for (Condition condition : this.conditions) {
|
|
||||||
if (!condition.requiredModId().isEmpty() && !PlatformFunctions.isModLoaded(condition.requiredModId()))
|
|
||||||
this.conditionsMet = false;
|
|
||||||
String requiredOption = condition.requiredOption().contains(":") ? condition.requiredOption() : (this.modid + ":" + condition.requiredOption());
|
|
||||||
if (entries.get(requiredOption) instanceof EntryInfo info)
|
|
||||||
this.conditionsMet &= List.of(condition.requiredValue()).contains(info.tempValue);
|
|
||||||
if (!this.conditionsMet) break;
|
|
||||||
}
|
|
||||||
if (prevConditionState != this.conditionsMet) configInstances.get(modid).reloadScreen = true;
|
|
||||||
}
|
|
||||||
public <T> void writeList(int index, T value) {
|
|
||||||
var list = (List<T>) this.value;
|
|
||||||
if (index >= list.size())
|
|
||||||
list.add(value);
|
|
||||||
else list.set(index, value);
|
|
||||||
}
|
|
||||||
public Tooltip getTooltip(boolean isButton) {
|
|
||||||
String key = this.modid + ".midnightconfig."+this.fieldName+(!isButton ? ".label" : "" )+".tooltip";
|
|
||||||
return Tooltip.of(isButton && this.error != null ? this.error : I18n.hasTranslation(key) ? Text.translatable(key) : Text.empty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//public static final Map<String, Class<? extends MidnightConfig>> configClass = new HashMap<>();
|
|
||||||
public static final Map<String, MidnightConfig> configInstances = new HashMap<>();
|
|
||||||
protected String modid;
|
|
||||||
public Class<? extends MidnightConfig> configClass;
|
|
||||||
|
|
||||||
private static final Gson gson = new GsonBuilder()
|
private static final Gson gson = new GsonBuilder()
|
||||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT).excludeFieldsWithModifiers(Modifier.PRIVATE)
|
.excludeFieldsWithModifiers(Modifier.TRANSIENT).excludeFieldsWithModifiers(Modifier.PRIVATE)
|
||||||
.addSerializationExclusionStrategy(new NonEntryExclusionStrategy())
|
.addSerializationExclusionStrategy(new ExclusionStrategy() {
|
||||||
|
public boolean shouldSkipClass(Class<?> clazz) { return false; }
|
||||||
|
public boolean shouldSkipField(FieldAttributes fieldAttributes) { return fieldAttributes.getAnnotation(Entry.class) == null; }
|
||||||
|
})
|
||||||
.registerTypeAdapter(Identifier.class, new TypeAdapter<Identifier>() {
|
.registerTypeAdapter(Identifier.class, new TypeAdapter<Identifier>() {
|
||||||
public void write(JsonWriter out, Identifier id) throws IOException { out.value(id.toString()); }
|
public void write(JsonWriter out, Identifier id) throws IOException { out.value(id.toString()); }
|
||||||
public Identifier read(JsonReader in) throws IOException { return Identifier.of(in.nextString()); }
|
public Identifier read(JsonReader in) throws IOException { return Identifier.of(in.nextString()); }
|
||||||
}).setPrettyPrinting().create();
|
}).setPrettyPrinting().create();
|
||||||
|
|
||||||
public void loadValuesFromJson() {
|
protected static final LinkedHashMap<String, EntryInfo> entries = new LinkedHashMap<>(); // modid:fieldName -> EntryInfo
|
||||||
|
|
||||||
|
public static final Map<String, MidnightConfig> configInstances = new HashMap<>();
|
||||||
|
|
||||||
|
protected String modid;
|
||||||
|
protected boolean reloadScreen = false;
|
||||||
|
public Class<? extends MidnightConfig> configClass;
|
||||||
|
|
||||||
|
public static <T extends MidnightConfig> T createInstance(String modid, Class<? extends MidnightConfig> configClass) { // This is basically an argumented constructor without the requirement of having one in each config class
|
||||||
try {
|
try {
|
||||||
gson.fromJson(Files.newBufferedReader(getJsonFilePath()), configClass);
|
T instance = (T) configClass.getDeclaredConstructor().newInstance();
|
||||||
|
instance.modid = modid;
|
||||||
|
instance.configClass = configClass;
|
||||||
|
configInstances.put(modid, instance);
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) { throw new RuntimeException(e); }
|
||||||
write(modid);
|
|
||||||
}
|
|
||||||
entries.values().forEach(info -> {
|
|
||||||
if (info.field != null && info.entry != null) {
|
|
||||||
try {
|
|
||||||
info.value = info.field.get(null) == null ? info.defaultValue : info.field.get(null);
|
|
||||||
info.tempValue = info.toTemporaryValue();
|
|
||||||
info.updateConditions();
|
|
||||||
} catch (IllegalAccessException ignored) {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init(String modid, Class<? extends MidnightConfig> config) {
|
public static void init(String modid, Class<? extends MidnightConfig> config) {
|
||||||
MidnightConfig instance = createInstance(modid, config);
|
MidnightConfig instance = createInstance(modid, config);
|
||||||
|
|
||||||
@@ -181,6 +81,7 @@ public abstract class MidnightConfig {
|
|||||||
}
|
}
|
||||||
instance.loadValuesFromJson();
|
instance.loadValuesFromJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
private void initClient(String modid, Field field, EntryInfo info) {
|
private void initClient(String modid, Field field, EntryInfo info) {
|
||||||
Entry e = info.entry;
|
Entry e = info.entry;
|
||||||
@@ -207,6 +108,7 @@ public abstract class MidnightConfig {
|
|||||||
}
|
}
|
||||||
entries.put(key, info);
|
entries.put(key, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Class<?> getUnderlyingType(Field field) {
|
public static Class<?> getUnderlyingType(Field field) {
|
||||||
Class<?> rawType = field.getType();
|
Class<?> rawType = field.getType();
|
||||||
if (field.getType() == List.class)
|
if (field.getType() == List.class)
|
||||||
@@ -215,13 +117,6 @@ public abstract class MidnightConfig {
|
|||||||
} catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; }
|
} catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private Text getEnumTranslatableText(Object value, EntryInfo info) {
|
|
||||||
if (value instanceof TranslatableOption translatableOption) return translatableOption.getText();
|
|
||||||
|
|
||||||
String translationKey = "%s.midnightconfig.enum.%s.%s".formatted(modid, info.dataType.getSimpleName(), info.toTemporaryValue());
|
|
||||||
return I18n.hasTranslation(translationKey) ? Text.translatable(translationKey) : Text.literal(info.toTemporaryValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void textField(EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
|
private static void textField(EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
|
||||||
boolean isNumber = pattern != null;
|
boolean isNumber = pattern != null;
|
||||||
info.function = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
|
info.function = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
|
||||||
@@ -259,16 +154,30 @@ public abstract class MidnightConfig {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T extends MidnightConfig> T createInstance(String modid, Class<? extends MidnightConfig> configClass) {
|
|
||||||
if (configInstances.containsKey(modid)) return (T) configInstances.get(modid);
|
protected Text getEnumTranslatableText(Object value, EntryInfo info) {
|
||||||
|
if (value instanceof TranslatableOption translatableOption) return translatableOption.getText();
|
||||||
|
|
||||||
|
String translationKey = "%s.midnightconfig.enum.%s.%s".formatted(modid, info.dataType.getSimpleName(), info.toTemporaryValue());
|
||||||
|
return I18n.hasTranslation(translationKey) ? Text.translatable(translationKey) : Text.literal(info.toTemporaryValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadValuesFromJson() {
|
||||||
try {
|
try {
|
||||||
T instance = (T) configClass.getDeclaredConstructor().newInstance();
|
gson.fromJson(Files.newBufferedReader(getJsonFilePath()), configClass);
|
||||||
instance.modid = modid;
|
|
||||||
instance.configClass = configClass;
|
|
||||||
configInstances.put(modid, instance);
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
catch (Exception e) { throw new RuntimeException(e); }
|
catch (Exception e) {
|
||||||
|
write(modid);
|
||||||
|
}
|
||||||
|
entries.values().forEach(info -> {
|
||||||
|
if (info.field != null && info.entry != null) {
|
||||||
|
try {
|
||||||
|
info.value = info.field.get(null) == null ? info.defaultValue : info.field.get(null);
|
||||||
|
info.tempValue = info.toTemporaryValue();
|
||||||
|
info.updateConditions();
|
||||||
|
} catch (IllegalAccessException ignored) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void write(String modid) {
|
public static void write(String modid) {
|
||||||
@@ -307,327 +216,6 @@ public abstract class MidnightConfig {
|
|||||||
public Screen getScreen(Screen parent) {
|
public Screen getScreen(Screen parent) {
|
||||||
return new MidnightConfigScreen(parent, modid);
|
return new MidnightConfigScreen(parent, modid);
|
||||||
}
|
}
|
||||||
@Environment(EnvType.CLIENT)
|
|
||||||
public static class MidnightConfigScreen extends Screen {
|
|
||||||
public MidnightConfig instance;
|
|
||||||
public final String translationPrefix, modid;
|
|
||||||
public final Screen parent;
|
|
||||||
public MidnightConfigListWidget list;
|
|
||||||
public TabManager tabManager = new TabManager(a -> {}, a -> {});
|
|
||||||
public Map<String, Tab> tabs = new LinkedHashMap<>();
|
|
||||||
public Tab prevTab;
|
|
||||||
public TabNavigationWidget tabNavigation;
|
|
||||||
public ButtonWidget done;
|
|
||||||
public double scrollProgress = 0d;
|
|
||||||
|
|
||||||
protected MidnightConfigScreen(Screen parent, String modid) {
|
|
||||||
super(Text.translatable(modid + ".midnightconfig.title"));
|
|
||||||
this.parent = parent; this.modid = modid;
|
|
||||||
this.translationPrefix = modid + ".midnightconfig.";
|
|
||||||
this.instance = configInstances.get(modid);
|
|
||||||
instance.loadValuesFromJson();
|
|
||||||
entries.values().forEach(info -> {
|
|
||||||
if (info.modid.equals(modid)) {
|
|
||||||
String tabId = info.entry != null ? info.entry.category() : info.comment.category();
|
|
||||||
String name = translationPrefix + "category." + tabId;
|
|
||||||
if (!I18n.hasTranslation(name) && tabId.equals("default"))
|
|
||||||
name = translationPrefix + "title";
|
|
||||||
if (!tabs.containsKey(name)) {
|
|
||||||
info.tab = new GridScreenTab(Text.translatable(name));
|
|
||||||
tabs.put(name, info.tab);
|
|
||||||
} else info.tab = tabs.get(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tabNavigation = TabNavigationWidget.builder(tabManager, this.width).tabs(tabs.values().toArray(new Tab[0])).build();
|
|
||||||
tabNavigation.selectTab(0, false);
|
|
||||||
tabNavigation.init();
|
|
||||||
prevTab = tabManager.getCurrentTab();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Real Time config update //
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
super.tick();
|
|
||||||
if (prevTab != null && prevTab != tabManager.getCurrentTab()) {
|
|
||||||
prevTab = tabManager.getCurrentTab();
|
|
||||||
updateList();
|
|
||||||
list.setScrollY(0);
|
|
||||||
}
|
|
||||||
scrollProgress = list.getScrollY();
|
|
||||||
for (EntryInfo info : entries.values())
|
|
||||||
info.updateFieldValue();
|
|
||||||
updateButtons();
|
|
||||||
if (instance.reloadScreen) {
|
|
||||||
updateList();
|
|
||||||
instance.reloadScreen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void updateButtons() {
|
|
||||||
if (this.list == null) return;
|
|
||||||
|
|
||||||
for (ButtonEntry entry : this.list.children()) {
|
|
||||||
if (entry.buttons != null && entry.buttons.size() > 1 && entry.info.field != null) {
|
|
||||||
if (entry.buttons.get(0) instanceof ClickableWidget widget)
|
|
||||||
if (widget.isFocused() || widget.isHovered())
|
|
||||||
widget.setTooltip(entry.info.getTooltip(true));
|
|
||||||
if (entry.buttons.get(1) instanceof ButtonWidget button)
|
|
||||||
button.active = !Objects.equals(String.valueOf(entry.info.value), String.valueOf(entry.info.defaultValue)) && entry.info.conditionsMet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean keyPressed(KeyInput input) {
|
|
||||||
return this.tabNavigation.keyPressed(input) || super.keyPressed(input);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
instance.loadValuesFromJson();
|
|
||||||
entries.values().forEach(info -> {
|
|
||||||
info.error = null; info.value = null; info.tempValue = null; info.actionButton = null; info.listIndex = 0; info.tab = null; info.inLimits = true;
|
|
||||||
});
|
|
||||||
Objects.requireNonNull(client).setScreen(parent);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void init() {
|
|
||||||
super.init();
|
|
||||||
tabNavigation.setWidth(this.width);
|
|
||||||
tabNavigation.init();
|
|
||||||
if (tabs.size() > 1)
|
|
||||||
this.addDrawableChild(tabNavigation);
|
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.CANCEL, button -> this.close()).dimensions(this.width / 2 - 154, this.height - 26, 150, 20).build());
|
|
||||||
done = this.addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, (button) -> {
|
|
||||||
for (EntryInfo info : entries.values())
|
|
||||||
if (info.modid.equals(modid))
|
|
||||||
info.updateFieldValue();
|
|
||||||
write(modid);
|
|
||||||
close();
|
|
||||||
}).dimensions(this.width / 2 + 4, this.height - 26, 150, 20).build());
|
|
||||||
|
|
||||||
this.list = new MidnightConfigListWidget(this.client, this.width, this.height - 57, 24, 25);
|
|
||||||
this.addSelectableChild(this.list);
|
|
||||||
updateList();
|
|
||||||
if (tabs.size() > 1)
|
|
||||||
list.renderHeaderSeparator = false;
|
|
||||||
}
|
|
||||||
public void updateList() {
|
|
||||||
this.list.clear();
|
|
||||||
instance.onTabInit(prevTab.getTitle().getContent() instanceof TranslatableTextContent translatable ?
|
|
||||||
translatable.getKey().substring(translatable.getKey().lastIndexOf('.')+1) : prevTab.getTitle().toString(), list, this);
|
|
||||||
for (EntryInfo info : entries.values()) {
|
|
||||||
info.updateConditions();
|
|
||||||
if (!info.conditionsMet) {
|
|
||||||
boolean visibleButLocked = false;
|
|
||||||
for (Condition condition : info.conditions)
|
|
||||||
visibleButLocked |= condition.visibleButLocked();
|
|
||||||
if (!visibleButLocked) continue;
|
|
||||||
}
|
|
||||||
if (info.modid.equals(modid) && (info.tab == null || info.tab == tabManager.getCurrentTab())) {
|
|
||||||
Text name = Objects.requireNonNullElseGet(info.name, () -> Text.translatable(translationPrefix + info.fieldName));
|
|
||||||
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
|
|
||||||
info.value = info.defaultValue;
|
|
||||||
info.listIndex = 0;
|
|
||||||
info.tempValue = info.toTemporaryValue();
|
|
||||||
updateList();
|
|
||||||
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
|
|
||||||
resetButton.setPosition(width - 205 + 150 + 25, 0);
|
|
||||||
|
|
||||||
if (info.function != null) {
|
|
||||||
ClickableWidget widget;
|
|
||||||
Entry e = info.entry;
|
|
||||||
if (info.function instanceof Map.Entry) { // Enums & booleans
|
|
||||||
var values = (Map.Entry<ButtonWidget.PressAction, Function<Object, Text>>) info.function;
|
|
||||||
if (info.dataType.isEnum()) {
|
|
||||||
values.setValue(value -> instance.getEnumTranslatableText(value, info));
|
|
||||||
}
|
|
||||||
widget = ButtonWidget.builder(values.getValue().apply(info.value), values.getKey()).dimensions(width - 185, 0, 150, 20).tooltip(info.getTooltip(true)).build();
|
|
||||||
} else if (e.isSlider())
|
|
||||||
widget = new MidnightSliderWidget(width - 185, 0, 150, 20, Text.of(info.tempValue), (Double.parseDouble(info.tempValue) - e.min()) / (e.max() - e.min()), info);
|
|
||||||
else
|
|
||||||
widget = new TextFieldWidget(textRenderer, width - 185, 0, 150, 20, Text.empty());
|
|
||||||
|
|
||||||
if (widget instanceof TextFieldWidget textField) {
|
|
||||||
textField.setMaxLength(e.width());
|
|
||||||
textField.setText(info.tempValue);
|
|
||||||
Predicate<String> processor = ((BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) info.function).apply(textField, done);
|
|
||||||
textField.setTextPredicate(processor);
|
|
||||||
}
|
|
||||||
widget.setTooltip(info.getTooltip(true));
|
|
||||||
|
|
||||||
ButtonWidget cycleButton = null;
|
|
||||||
if (info.field.getType() == List.class) {
|
|
||||||
cycleButton = ButtonWidget.builder(Text.literal(String.valueOf(info.listIndex)).formatted(Formatting.GOLD), (button -> {
|
|
||||||
var values = (List<?>) info.value;
|
|
||||||
values.remove("");
|
|
||||||
info.listIndex = info.listIndex != values.size() ? info.listIndex + 1 : 0;
|
|
||||||
info.tempValue = info.listIndex != values.size() ? info.toTemporaryValue() : "";
|
|
||||||
updateList();
|
|
||||||
})).dimensions(width - 185, 0, 20, 20).build();
|
|
||||||
}
|
|
||||||
if (e.isColor()) {
|
|
||||||
ButtonWidget colorButton = ButtonWidget.builder(Text.literal("⬛"),
|
|
||||||
button -> new Thread(() -> {
|
|
||||||
Color newColor = JColorChooser.showDialog(null, Text.translatable("midnightconfig.colorChooser.title").getString(), Color.decode(!Objects.equals(info.tempValue, "") ? info.tempValue : "#FFFFFF"));
|
|
||||||
if (newColor != null) {
|
|
||||||
info.setValue("#" + Integer.toHexString(newColor.getRGB()).substring(2));
|
|
||||||
updateList();
|
|
||||||
}
|
|
||||||
}).start()
|
|
||||||
).dimensions(width - 185, 0, 20, 20).build();
|
|
||||||
try { colorButton.setMessage(Text.literal("⬛").setStyle(Style.EMPTY.withColor(Color.decode(info.tempValue).getRGB())));
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
info.actionButton = colorButton;
|
|
||||||
} else if (e.selectionMode() > -1) {
|
|
||||||
ButtonWidget explorerButton = TextIconButtonWidget.builder(Text.empty(),
|
|
||||||
button -> new Thread(() -> {
|
|
||||||
JFileChooser fileChooser = new JFileChooser(info.tempValue);
|
|
||||||
fileChooser.setFileSelectionMode(e.selectionMode());
|
|
||||||
fileChooser.setDialogType(e.fileChooserType());
|
|
||||||
fileChooser.setDialogTitle(Text.translatable(translationPrefix + info.fieldName + ".fileChooser").getString());
|
|
||||||
if ((e.selectionMode() == JFileChooser.FILES_ONLY || e.selectionMode() == JFileChooser.FILES_AND_DIRECTORIES) && Arrays.stream(e.fileExtensions()).noneMatch("*"::equals))
|
|
||||||
fileChooser.setFileFilter(new FileNameExtensionFilter(
|
|
||||||
Text.translatable(translationPrefix + info.fieldName + ".fileFilter").getString(), e.fileExtensions()));
|
|
||||||
if (fileChooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION) {
|
|
||||||
info.setValue(fileChooser.getSelectedFile().getAbsolutePath());
|
|
||||||
updateList();
|
|
||||||
}
|
|
||||||
}).start(), true
|
|
||||||
).texture(Identifier.of("midnightlib", "icon/explorer"), 12, 12).dimension(20, 20).build();
|
|
||||||
explorerButton.setPosition(width - 185, 0);
|
|
||||||
info.actionButton = explorerButton;
|
|
||||||
}
|
|
||||||
List<ClickableWidget> widgets = Lists.newArrayList(widget, resetButton);
|
|
||||||
|
|
||||||
if (info.actionButton != null) {
|
|
||||||
if (Util.getOperatingSystem() == Util.OperatingSystem.OSX) info.actionButton.active = false;
|
|
||||||
widget.setWidth(widget.getWidth() - 22);
|
|
||||||
widget.setX(widget.getX() + 22);
|
|
||||||
widgets.add(info.actionButton);
|
|
||||||
}
|
|
||||||
if (cycleButton != null) {
|
|
||||||
if (info.actionButton != null) info.actionButton.setX(info.actionButton.getX() + 22);
|
|
||||||
widget.setWidth(widget.getWidth() - 22);
|
|
||||||
widget.setX(widget.getX() + 22);
|
|
||||||
widgets.add(cycleButton);
|
|
||||||
}
|
|
||||||
if (!info.conditionsMet) widgets.forEach(w -> w.active = false);
|
|
||||||
this.list.addButton(widgets, name, info);
|
|
||||||
} else this.list.addButton(List.of(), name, info);
|
|
||||||
} list.setScrollY(scrollProgress);
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
|
||||||
super.render(context, mouseX, mouseY, delta);
|
|
||||||
this.list.render(context, mouseX, mouseY, delta);
|
|
||||||
if (tabs.size() < 2) context.drawCenteredTextWithShadow(textRenderer, title, width / 2, 10, 0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Environment(EnvType.CLIENT)
|
|
||||||
public static class MidnightConfigListWidget extends ElementListWidget<ButtonEntry> {
|
|
||||||
public boolean renderHeaderSeparator = true;
|
|
||||||
|
|
||||||
public MidnightConfigListWidget(MinecraftClient client, int width, int height, int y, int itemHeight) {
|
|
||||||
super(client, width, height, y, itemHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int getScrollbarX() {
|
|
||||||
return this.width -7;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void drawHeaderAndFooterSeparators(DrawContext context) {
|
|
||||||
if (renderHeaderSeparator)
|
|
||||||
super.drawHeaderAndFooterSeparators(context);
|
|
||||||
else
|
|
||||||
context.drawTexture(RenderPipelines.GUI_TEXTURED, this.client.world == null ? Screen.FOOTER_SEPARATOR_TEXTURE : Screen.INWORLD_FOOTER_SEPARATOR_TEXTURE, this.getX(), this.getBottom(), 0, 0, this.getWidth(), 2, 32, 2);
|
|
||||||
}
|
|
||||||
public void addButton(List<ClickableWidget> buttons, Text text, EntryInfo info) {
|
|
||||||
this.addEntry(new ButtonEntry(buttons, text, info));
|
|
||||||
}
|
|
||||||
public void clear() {
|
|
||||||
this.clearEntries();
|
|
||||||
}
|
|
||||||
@Override public int getRowWidth() {
|
|
||||||
return 10000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static class ButtonEntry extends ElementListWidget.Entry<ButtonEntry> {
|
|
||||||
private static final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
|
|
||||||
public final Text text;
|
|
||||||
public final List<ClickableWidget> buttons;
|
|
||||||
public final EntryInfo info;
|
|
||||||
public boolean centered = false;
|
|
||||||
public MultilineTextWidget title;
|
|
||||||
|
|
||||||
public ButtonEntry(List<ClickableWidget> buttons, Text text, EntryInfo info) {
|
|
||||||
this.buttons = buttons;
|
|
||||||
this.text = text;
|
|
||||||
this.info = info;
|
|
||||||
if (info != null && info.comment != null)
|
|
||||||
this.centered = info.comment.centered();
|
|
||||||
int scaledWidth = MinecraftClient.getInstance().getWindow().getScaledWidth();
|
|
||||||
|
|
||||||
if (text != null && (!text.getString().contains("spacer") || !buttons.isEmpty())) {
|
|
||||||
title = new MultilineTextWidget((centered) ? (scaledWidth / 2 - (textRenderer.getWidth(text) / 2)) : 12, 0, Text.of(text), textRenderer);
|
|
||||||
title.setCentered(centered);
|
|
||||||
if (info != null)
|
|
||||||
title.setTooltip(info.getTooltip(false));
|
|
||||||
title.setMaxWidth(!buttons.isEmpty() ? buttons.get(buttons.size() > 2 ? buttons.size()-1 : 0).getX() - 16 : scaledWidth - 24);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void render(DrawContext context, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
|
||||||
buttons.forEach(b -> {
|
|
||||||
b.setY(this.getY());
|
|
||||||
b.render(context, mouseX, mouseY, tickDelta);
|
|
||||||
});
|
|
||||||
if (title != null) {
|
|
||||||
title.setY(this.getY()+5);
|
|
||||||
title.render(context, mouseX, mouseY, tickDelta);
|
|
||||||
|
|
||||||
if (info.entry != null && !this.buttons.isEmpty() && this.buttons.getFirst() instanceof ClickableWidget widget) {
|
|
||||||
int idMode = this.info.entry.idMode();
|
|
||||||
if (idMode != -1) context.drawItem(idMode == 0 ?
|
|
||||||
Registries.ITEM.get(Identifier.tryParse(this.info.tempValue)).getDefaultStack()
|
|
||||||
: Registries.BLOCK.get(Identifier.tryParse(this.info.tempValue)).asItem().getDefaultStack(),
|
|
||||||
widget.getX() + widget.getWidth() - 18, this.getY() + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean mouseClicked(Click click, boolean doubled) {
|
|
||||||
if (this.info != null && this.info.comment != null && !this.info.comment.url().isBlank())
|
|
||||||
ConfirmLinkScreen.open(MinecraftClient.getInstance().currentScreen, this.info.comment.url(), true);
|
|
||||||
return super.mouseClicked(click, doubled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<? extends Element> children() {return Lists.newArrayList(buttons);}
|
|
||||||
public List<? extends Selectable> selectableChildren() {return Lists.newArrayList(buttons);}
|
|
||||||
}
|
|
||||||
public static class MidnightSliderWidget extends SliderWidget {
|
|
||||||
private final EntryInfo info; private final Entry e;
|
|
||||||
public MidnightSliderWidget(int x, int y, int width, int height, Text text, double value, EntryInfo info) {
|
|
||||||
super(x, y, width, height, text, value);
|
|
||||||
this.e = info.entry;
|
|
||||||
this.info = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateMessage() { this.setMessage(Text.of(info.tempValue)); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyValue() {
|
|
||||||
if (info.dataType == int.class) info.setValue(((Number) (e.min() + value * (e.max() - e.min()))).intValue());
|
|
||||||
else if (info.dataType == double.class) info.setValue(Math.round((e.min() + value * (e.max() - e.min())) * (double) e.precision()) / (double) e.precision());
|
|
||||||
else if (info.dataType == float.class) info.setValue(Math.round((e.min() + value * (e.max() - e.min())) * (float) e.precision()) / (float) e.precision());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static class NonEntryExclusionStrategy implements ExclusionStrategy {
|
|
||||||
public boolean shouldSkipClass(Class<?> clazz) { return false; }
|
|
||||||
public boolean shouldSkipField(FieldAttributes fieldAttributes) { return fieldAttributes.getAnnotation(Entry.class) == null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry Annotation<br>
|
* Entry Annotation<br>
|
||||||
@@ -668,19 +256,25 @@ public abstract class MidnightConfig {
|
|||||||
@Deprecated String requiredMod() default "";
|
@Deprecated String requiredMod() default "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Client {}
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Client {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the entry in config screens, but still makes it accessible through the command {@code /midnightconfig MOD_ID ENTRY} and directly editing the config file.
|
* Hides the entry in config screens, but still makes it accessible through the command {@code /midnightconfig MOD_ID ENTRY} and directly editing the config file.
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Server {}
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Server {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the entry entirely.
|
* Hides the entry entirely.
|
||||||
* Accessible only through directly editing the config file.
|
* Accessible only through directly editing the config file.
|
||||||
* Perfect for saving persistent internal data.
|
* Perfect for saving persistent internal data.
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Hidden {}
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Hidden {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comment Annotation<br>
|
* Comment Annotation<br>
|
||||||
@@ -689,7 +283,9 @@ public abstract class MidnightConfig {
|
|||||||
* - <b>{@link Comment#name()}</b>: Will be used instead of the default translation key, if not empty<br>
|
* - <b>{@link Comment#name()}</b>: Will be used instead of the default translation key, if not empty<br>
|
||||||
* - <b>{@link Comment#url()}</b>: The url of the comment should link to in the config screen (none if left empty)<br>
|
* - <b>{@link Comment#url()}</b>: The url of the comment should link to in the config screen (none if left empty)<br>
|
||||||
* */
|
* */
|
||||||
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Comment {
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Comment {
|
||||||
boolean centered() default false;
|
boolean centered() default false;
|
||||||
String category() default "default";
|
String category() default "default";
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package eu.midnightdust.lib.config;
|
||||||
|
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gl.RenderPipelines;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||||
|
import net.minecraft.client.gui.widget.ElementListWidget;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Environment(EnvType.CLIENT)
|
||||||
|
public class MidnightConfigListWidget extends ElementListWidget<ButtonEntry> {
|
||||||
|
public boolean renderHeaderSeparator = true;
|
||||||
|
|
||||||
|
public MidnightConfigListWidget(MinecraftClient client, int width, int height, int y, int itemHeight) {
|
||||||
|
super(client, width, height, y, itemHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getScrollbarX() {
|
||||||
|
return this.width - 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawHeaderAndFooterSeparators(DrawContext context) {
|
||||||
|
if (renderHeaderSeparator)
|
||||||
|
super.drawHeaderAndFooterSeparators(context);
|
||||||
|
else
|
||||||
|
context.drawTexture(RenderPipelines.GUI_TEXTURED, this.client.world == null ? Screen.FOOTER_SEPARATOR_TEXTURE : Screen.INWORLD_FOOTER_SEPARATOR_TEXTURE, this.getX(), this.getBottom(), 0, 0, this.getWidth(), 2, 32, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addButton(List<ClickableWidget> buttons, Text text, EntryInfo info) {
|
||||||
|
this.addEntry(new ButtonEntry(buttons, text, info));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.clearEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowWidth() {
|
||||||
|
return 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
package eu.midnightdust.lib.config;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.tab.GridScreenTab;
|
||||||
|
import net.minecraft.client.gui.tab.Tab;
|
||||||
|
import net.minecraft.client.gui.tab.TabManager;
|
||||||
|
import net.minecraft.client.gui.widget.*;
|
||||||
|
import net.minecraft.client.input.KeyInput;
|
||||||
|
import net.minecraft.client.resource.language.I18n;
|
||||||
|
import net.minecraft.screen.ScreenTexts;
|
||||||
|
import net.minecraft.text.Style;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.text.TranslatableTextContent;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.Util;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Environment(EnvType.CLIENT)
|
||||||
|
public class MidnightConfigScreen extends Screen {
|
||||||
|
public MidnightConfig instance;
|
||||||
|
public final String translationPrefix, modid;
|
||||||
|
public final Screen parent;
|
||||||
|
public MidnightConfigListWidget list;
|
||||||
|
public TabManager tabManager = new TabManager(a -> {
|
||||||
|
}, a -> {
|
||||||
|
});
|
||||||
|
public Map<String, Tab> tabs = new LinkedHashMap<>();
|
||||||
|
public Tab prevTab;
|
||||||
|
public TabNavigationWidget tabNavigation;
|
||||||
|
public ButtonWidget done;
|
||||||
|
public double scrollProgress = 0d;
|
||||||
|
|
||||||
|
public MidnightConfigScreen(Screen parent, String modid) {
|
||||||
|
super(Text.translatable(modid + ".midnightconfig.title"));
|
||||||
|
this.parent = parent;
|
||||||
|
this.modid = modid;
|
||||||
|
this.translationPrefix = modid + ".midnightconfig.";
|
||||||
|
this.instance = MidnightConfig.configInstances.get(modid);
|
||||||
|
instance.loadValuesFromJson();
|
||||||
|
MidnightConfig.entries.values().forEach(info -> {
|
||||||
|
if (Objects.equals(info.modid, modid)) {
|
||||||
|
String tabId = info.entry != null ? info.entry.category() : info.comment.category();
|
||||||
|
String name = translationPrefix + "category." + tabId;
|
||||||
|
if (!I18n.hasTranslation(name) && tabId.equals("default"))
|
||||||
|
name = translationPrefix + "title";
|
||||||
|
if (!tabs.containsKey(name)) {
|
||||||
|
info.tab = new GridScreenTab(Text.translatable(name));
|
||||||
|
tabs.put(name, info.tab);
|
||||||
|
} else info.tab = tabs.get(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tabNavigation = TabNavigationWidget.builder(tabManager, this.width).tabs(tabs.values().toArray(new Tab[0])).build();
|
||||||
|
tabNavigation.selectTab(0, false);
|
||||||
|
tabNavigation.init();
|
||||||
|
prevTab = tabManager.getCurrentTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real Time config update //
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (prevTab != null && prevTab != tabManager.getCurrentTab()) {
|
||||||
|
prevTab = tabManager.getCurrentTab();
|
||||||
|
updateList();
|
||||||
|
list.setScrollY(0);
|
||||||
|
}
|
||||||
|
scrollProgress = list.getScrollY();
|
||||||
|
for (EntryInfo info : MidnightConfig.entries.values())
|
||||||
|
info.updateFieldValue();
|
||||||
|
updateButtons();
|
||||||
|
if (instance.reloadScreen) {
|
||||||
|
updateList();
|
||||||
|
instance.reloadScreen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateButtons() {
|
||||||
|
if (this.list == null) return;
|
||||||
|
|
||||||
|
for (ButtonEntry entry : this.list.children()) {
|
||||||
|
if (entry.buttons != null && entry.buttons.size() > 1 && entry.info.field != null) {
|
||||||
|
if (entry.buttons.get(0) instanceof ClickableWidget widget)
|
||||||
|
if (widget.isFocused() || widget.isHovered())
|
||||||
|
widget.setTooltip(entry.info.getTooltip(true));
|
||||||
|
if (entry.buttons.get(1) instanceof ButtonWidget button)
|
||||||
|
button.active = !Objects.equals(String.valueOf(entry.info.value), String.valueOf(entry.info.defaultValue)) && entry.info.conditionsMet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(KeyInput input) {
|
||||||
|
return this.tabNavigation.keyPressed(input) || super.keyPressed(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
instance.loadValuesFromJson();
|
||||||
|
MidnightConfig.entries.values().forEach(info -> {
|
||||||
|
info.error = null;
|
||||||
|
info.value = null;
|
||||||
|
info.tempValue = null;
|
||||||
|
info.actionButton = null;
|
||||||
|
info.listIndex = 0;
|
||||||
|
info.tab = null;
|
||||||
|
info.inLimits = true;
|
||||||
|
});
|
||||||
|
Objects.requireNonNull(client).setScreen(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
super.init();
|
||||||
|
tabNavigation.setWidth(this.width);
|
||||||
|
tabNavigation.init();
|
||||||
|
if (tabs.size() > 1)
|
||||||
|
this.addDrawableChild(tabNavigation);
|
||||||
|
|
||||||
|
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.CANCEL, button -> this.close()).dimensions(this.width / 2 - 154, this.height - 26, 150, 20).build());
|
||||||
|
done = this.addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, (button) -> {
|
||||||
|
for (EntryInfo info : MidnightConfig.entries.values())
|
||||||
|
if (info.modid.equals(modid))
|
||||||
|
info.updateFieldValue();
|
||||||
|
MidnightConfig.write(modid);
|
||||||
|
close();
|
||||||
|
}).dimensions(this.width / 2 + 4, this.height - 26, 150, 20).build());
|
||||||
|
|
||||||
|
this.list = new MidnightConfigListWidget(this.client, this.width, this.height - 57, 24, 25);
|
||||||
|
this.addSelectableChild(this.list);
|
||||||
|
updateList();
|
||||||
|
if (tabs.size() > 1)
|
||||||
|
list.renderHeaderSeparator = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateList() {
|
||||||
|
this.list.clear();
|
||||||
|
instance.onTabInit(prevTab.getTitle().getContent() instanceof TranslatableTextContent translatable ?
|
||||||
|
translatable.getKey().substring(translatable.getKey().lastIndexOf('.') + 1) : prevTab.getTitle().toString(), list, this);
|
||||||
|
for (EntryInfo info : MidnightConfig.entries.values()) {
|
||||||
|
info.updateConditions();
|
||||||
|
if (!info.conditionsMet) {
|
||||||
|
boolean visibleButLocked = false;
|
||||||
|
for (MidnightConfig.Condition condition : info.conditions)
|
||||||
|
visibleButLocked |= condition.visibleButLocked();
|
||||||
|
if (!visibleButLocked) continue;
|
||||||
|
}
|
||||||
|
if (info.modid.equals(modid) && (info.tab == null || info.tab == tabManager.getCurrentTab())) {
|
||||||
|
Text name = Objects.requireNonNullElseGet(info.name, () -> Text.translatable(translationPrefix + info.fieldName));
|
||||||
|
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
|
||||||
|
info.value = info.defaultValue;
|
||||||
|
info.listIndex = 0;
|
||||||
|
info.tempValue = info.toTemporaryValue();
|
||||||
|
updateList();
|
||||||
|
}), true).texture(Identifier.of("midnightlib", "icon/reset"), 12, 12).dimension(20, 20).build();
|
||||||
|
resetButton.setPosition(width - 205 + 150 + 25, 0);
|
||||||
|
|
||||||
|
if (info.function != null) {
|
||||||
|
ClickableWidget widget;
|
||||||
|
MidnightConfig.Entry e = info.entry;
|
||||||
|
if (info.function instanceof Map.Entry) { // Enums & booleans
|
||||||
|
var values = (Map.Entry<ButtonWidget.PressAction, Function<Object, Text>>) info.function;
|
||||||
|
if (info.dataType.isEnum()) {
|
||||||
|
values.setValue(value -> instance.getEnumTranslatableText(value, info));
|
||||||
|
}
|
||||||
|
widget = ButtonWidget.builder(values.getValue().apply(info.value), values.getKey()).dimensions(width - 185, 0, 150, 20).tooltip(info.getTooltip(true)).build();
|
||||||
|
} else if (e.isSlider())
|
||||||
|
widget = new MidnightSliderWidget(width - 185, 0, 150, 20, Text.of(info.tempValue), (Double.parseDouble(info.tempValue) - e.min()) / (e.max() - e.min()), info);
|
||||||
|
else
|
||||||
|
widget = new TextFieldWidget(textRenderer, width - 185, 0, 150, 20, Text.empty());
|
||||||
|
|
||||||
|
if (widget instanceof TextFieldWidget textField) {
|
||||||
|
textField.setMaxLength(e.width());
|
||||||
|
textField.setText(info.tempValue);
|
||||||
|
Predicate<String> processor = ((BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) info.function).apply(textField, done);
|
||||||
|
textField.setTextPredicate(processor);
|
||||||
|
}
|
||||||
|
widget.setTooltip(info.getTooltip(true));
|
||||||
|
|
||||||
|
ButtonWidget cycleButton = null;
|
||||||
|
if (info.field.getType() == List.class) {
|
||||||
|
cycleButton = ButtonWidget.builder(Text.literal(String.valueOf(info.listIndex)).formatted(Formatting.GOLD), (button -> {
|
||||||
|
var values = (List<?>) info.value;
|
||||||
|
values.remove("");
|
||||||
|
info.listIndex = info.listIndex != values.size() ? info.listIndex + 1 : 0;
|
||||||
|
info.tempValue = info.listIndex != values.size() ? info.toTemporaryValue() : "";
|
||||||
|
updateList();
|
||||||
|
})).dimensions(width - 185, 0, 20, 20).build();
|
||||||
|
}
|
||||||
|
if (e.isColor()) {
|
||||||
|
ButtonWidget colorButton = ButtonWidget.builder(Text.literal("⬛"),
|
||||||
|
button -> new Thread(() -> {
|
||||||
|
Color newColor = JColorChooser.showDialog(null, Text.translatable("midnightconfig.colorChooser.title").getString(), Color.decode(!Objects.equals(info.tempValue, "") ? info.tempValue : "#FFFFFF"));
|
||||||
|
if (newColor != null) {
|
||||||
|
info.setValue("#" + Integer.toHexString(newColor.getRGB()).substring(2));
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
}).start()
|
||||||
|
).dimensions(width - 185, 0, 20, 20).build();
|
||||||
|
try {
|
||||||
|
colorButton.setMessage(Text.literal("⬛").setStyle(Style.EMPTY.withColor(Color.decode(info.tempValue).getRGB())));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
info.actionButton = colorButton;
|
||||||
|
} else if (e.selectionMode() > -1) {
|
||||||
|
ButtonWidget explorerButton = TextIconButtonWidget.builder(Text.empty(),
|
||||||
|
button -> new Thread(() -> {
|
||||||
|
JFileChooser fileChooser = new JFileChooser(info.tempValue);
|
||||||
|
fileChooser.setFileSelectionMode(e.selectionMode());
|
||||||
|
fileChooser.setDialogType(e.fileChooserType());
|
||||||
|
fileChooser.setDialogTitle(Text.translatable(translationPrefix + info.fieldName + ".fileChooser").getString());
|
||||||
|
if ((e.selectionMode() == JFileChooser.FILES_ONLY || e.selectionMode() == JFileChooser.FILES_AND_DIRECTORIES) && Arrays.stream(e.fileExtensions()).noneMatch("*"::equals))
|
||||||
|
fileChooser.setFileFilter(new FileNameExtensionFilter(
|
||||||
|
Text.translatable(translationPrefix + info.fieldName + ".fileFilter").getString(), e.fileExtensions()));
|
||||||
|
if (fileChooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION) {
|
||||||
|
info.setValue(fileChooser.getSelectedFile().getAbsolutePath());
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
}).start(), true
|
||||||
|
).texture(Identifier.of("midnightlib", "icon/explorer"), 12, 12).dimension(20, 20).build();
|
||||||
|
explorerButton.setPosition(width - 185, 0);
|
||||||
|
info.actionButton = explorerButton;
|
||||||
|
}
|
||||||
|
List<ClickableWidget> widgets = Lists.newArrayList(widget, resetButton);
|
||||||
|
|
||||||
|
if (info.actionButton != null) {
|
||||||
|
if (Util.getOperatingSystem() == Util.OperatingSystem.OSX) info.actionButton.active = false;
|
||||||
|
widget.setWidth(widget.getWidth() - 22);
|
||||||
|
widget.setX(widget.getX() + 22);
|
||||||
|
widgets.add(info.actionButton);
|
||||||
|
}
|
||||||
|
if (cycleButton != null) {
|
||||||
|
if (info.actionButton != null) info.actionButton.setX(info.actionButton.getX() + 22);
|
||||||
|
widget.setWidth(widget.getWidth() - 22);
|
||||||
|
widget.setX(widget.getX() + 22);
|
||||||
|
widgets.add(cycleButton);
|
||||||
|
}
|
||||||
|
if (!info.conditionsMet) widgets.forEach(w -> w.active = false);
|
||||||
|
this.list.addButton(widgets, name, info);
|
||||||
|
} else this.list.addButton(List.of(), name, info);
|
||||||
|
}
|
||||||
|
list.setScrollY(scrollProgress);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
super.render(context, mouseX, mouseY, delta);
|
||||||
|
this.list.render(context, mouseX, mouseY, delta);
|
||||||
|
if (tabs.size() < 2) context.drawCenteredTextWithShadow(textRenderer, title, width / 2, 10, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package eu.midnightdust.lib.config;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.widget.SliderWidget;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
public class MidnightSliderWidget extends SliderWidget {
|
||||||
|
private final EntryInfo info;
|
||||||
|
private final MidnightConfig.Entry e;
|
||||||
|
|
||||||
|
public MidnightSliderWidget(int x, int y, int width, int height, Text text, double value, EntryInfo info) {
|
||||||
|
super(x, y, width, height, text, value);
|
||||||
|
this.e = info.entry;
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateMessage() {
|
||||||
|
this.setMessage(Text.of(info.tempValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyValue() {
|
||||||
|
if (info.dataType == int.class) info.setValue(((Number) (e.min() + value * (e.max() - e.min()))).intValue());
|
||||||
|
else if (info.dataType == double.class)
|
||||||
|
info.setValue(Math.round((e.min() + value * (e.max() - e.min())) * (double) e.precision()) / (double) e.precision());
|
||||||
|
else if (info.dataType == float.class)
|
||||||
|
info.setValue(Math.round((e.min() + value * (e.max() - e.min())) * (float) e.precision()) / (float) e.precision());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package eu.midnightdust.test;
|
package eu.midnightdust.test;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import eu.midnightdust.lib.config.MidnightConfig;
|
import eu.midnightdust.lib.config.EntryInfo;
|
||||||
|
import eu.midnightdust.lib.config.MidnightConfigListWidget;
|
||||||
|
import eu.midnightdust.lib.config.MidnightConfigScreen;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.gui.tooltip.Tooltip;
|
import net.minecraft.client.gui.tooltip.Tooltip;
|
||||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||||
@@ -25,7 +27,7 @@ public class MidnightLibExtras {
|
|||||||
public static class KeybindButton extends ButtonWidget {
|
public static class KeybindButton extends ButtonWidget {
|
||||||
public static ButtonWidget focusedButton;
|
public static ButtonWidget focusedButton;
|
||||||
|
|
||||||
public static void add(KeyBinding binding, MidnightConfig.MidnightConfigListWidget list, MidnightConfig.MidnightConfigScreen screen) {
|
public static void add(KeyBinding binding, MidnightConfigListWidget list, MidnightConfigScreen screen) {
|
||||||
KeybindButton editButton = new KeybindButton(screen.width - 185, 0, 150, 20, binding);
|
KeybindButton editButton = new KeybindButton(screen.width - 185, 0, 150, 20, binding);
|
||||||
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
|
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
|
||||||
binding.setBoundKey(binding.getDefaultKey());
|
binding.setBoundKey(binding.getDefaultKey());
|
||||||
@@ -34,7 +36,7 @@ public class MidnightLibExtras {
|
|||||||
resetButton.setPosition(screen.width - 205 + 150 + 25, 0);
|
resetButton.setPosition(screen.width - 205 + 150 + 25, 0);
|
||||||
editButton.resetButton = resetButton;
|
editButton.resetButton = resetButton;
|
||||||
editButton.updateMessage(false);
|
editButton.updateMessage(false);
|
||||||
MidnightConfig.EntryInfo info = new MidnightConfig.EntryInfo(null, screen.modid);
|
EntryInfo info = new EntryInfo(null, screen.modid);
|
||||||
|
|
||||||
list.addButton(Lists.newArrayList(editButton, resetButton), Text.translatable(binding.getId()), info);
|
list.addButton(Lists.newArrayList(editButton, resetButton), Text.translatable(binding.getId()), info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package eu.midnightdust.test.config;
|
package eu.midnightdust.test.config;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import eu.midnightdust.lib.config.MidnightConfigListWidget;
|
||||||
|
import eu.midnightdust.lib.config.MidnightConfigScreen;
|
||||||
import eu.midnightdust.test.MidnightLibExtras;
|
import eu.midnightdust.test.MidnightLibExtras;
|
||||||
import eu.midnightdust.lib.config.MidnightConfig;
|
import eu.midnightdust.lib.config.MidnightConfig;
|
||||||
import net.minecraft.text.MutableText;
|
import net.minecraft.text.MutableText;
|
||||||
|
|||||||
Reference in New Issue
Block a user