mirror of
https://github.com/TeamMidnightDust/MidnightLib.git
synced 2025-12-15 17:05:09 +01:00
feat: conditions! + large cleanup
This commit is contained in:
@@ -40,27 +40,34 @@ public abstract class MidnightConfig {
|
|||||||
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 List<EntryInfo> entries = new ArrayList<>();
|
private static final List<EntryInfo> entries = new ArrayList<>();
|
||||||
|
private static boolean reloadScreen = false;
|
||||||
|
|
||||||
public static class EntryInfo {
|
public static class EntryInfo {
|
||||||
public Entry entry;
|
public Entry entry;
|
||||||
public Comment comment;
|
public Comment comment;
|
||||||
final Field field;
|
public Condition condition;
|
||||||
Class<?> dataType;
|
public final Field field;
|
||||||
|
public final Class<?> dataType;
|
||||||
|
public final String modid, fieldName;
|
||||||
int listIndex;
|
int listIndex;
|
||||||
Object defaultValue, value, function;
|
Object defaultValue, value, function;
|
||||||
String modid, fieldName, tempValue = ""; // The value visible in the config screen
|
String tempValue; // The value visible in the config screen
|
||||||
boolean inLimits = true;
|
boolean inLimits = true;
|
||||||
Text name, error;
|
Text name, error;
|
||||||
ClickableWidget actionButton; // color picker button / explorer button
|
ClickableWidget actionButton; // color picker button / explorer button
|
||||||
Tab tab;
|
Tab tab;
|
||||||
|
boolean conditionsMet = true;
|
||||||
|
|
||||||
public EntryInfo(Field field) {
|
public EntryInfo(Field field, String modid) {
|
||||||
this.field = field;
|
this.field = field; this.modid = modid;
|
||||||
if (field != null) {
|
if (field != null) {
|
||||||
this.fieldName = field.getName();
|
this.fieldName = field.getName(); this.dataType = getUnderlyingType(field);
|
||||||
this.entry = field.getAnnotation(Entry.class);
|
this.entry = field.getAnnotation(Entry.class); this.comment = field.getAnnotation(Comment.class); this.condition = field.getAnnotation(Condition.class);
|
||||||
this.comment = field.getAnnotation(Comment.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) {
|
public void setValue(Object value) {
|
||||||
if (this.field.getType() != List.class) { this.value = value;
|
if (this.field.getType() != List.class) { this.value = value;
|
||||||
@@ -73,7 +80,19 @@ public abstract class MidnightConfig {
|
|||||||
else try { return ((List<?>) this.value).get(this.listIndex).toString(); } catch (Exception ignored) {return "";}
|
else try { return ((List<?>) this.value).get(this.listIndex).toString(); } catch (Exception ignored) {return "";}
|
||||||
}
|
}
|
||||||
public void updateFieldValue() {
|
public void updateFieldValue() {
|
||||||
try { this.field.set(null, this.value); } catch (IllegalAccessException ignored) {}
|
try { if (this.field.get(null) != value) updateConditions(tempValue);
|
||||||
|
this.field.set(null, this.value);
|
||||||
|
} catch (IllegalAccessException ignored) {}
|
||||||
|
}
|
||||||
|
@SuppressWarnings("ConstantValue") //pertains to requiredModLoaded
|
||||||
|
public void updateConditions(String newTempValue) {
|
||||||
|
for (EntryInfo info : entries) {
|
||||||
|
boolean prevConditionState = info.conditionsMet;
|
||||||
|
if (info.condition != null && ((info.condition.requiredOption().contains(":") ? "" : info.modid + ":") + info.condition.requiredOption()).equals(this.modid + ":" + this.fieldName))
|
||||||
|
info.conditionsMet = Objects.equals(info.condition.requiredValue(), newTempValue);
|
||||||
|
if (info.condition != null && !info.condition.requiredModId().isEmpty() && !PlatformFunctions.isModLoaded(info.condition.requiredModId())) info.conditionsMet = false;
|
||||||
|
if (prevConditionState != info.conditionsMet) reloadScreen = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public <T> void writeList(int index, T value) {
|
public <T> void writeList(int index, T value) {
|
||||||
var list = (List<T>) this.value;
|
var list = (List<T>) this.value;
|
||||||
@@ -105,6 +124,7 @@ public abstract class MidnightConfig {
|
|||||||
|
|
||||||
for (EntryInfo info : entries) if (info.field != null && info.entry != null) {
|
for (EntryInfo info : entries) if (info.field != null && info.entry != null) {
|
||||||
try { info.value = info.field.get(null); info.tempValue = info.toTemporaryValue();
|
try { info.value = info.field.get(null); info.tempValue = info.toTemporaryValue();
|
||||||
|
info.updateConditions(info.tempValue);
|
||||||
} catch (IllegalAccessException ignored) {}
|
} catch (IllegalAccessException ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +133,7 @@ public abstract class MidnightConfig {
|
|||||||
configClass.put(modid, config);
|
configClass.put(modid, config);
|
||||||
|
|
||||||
for (Field field : config.getFields()) {
|
for (Field field : config.getFields()) {
|
||||||
EntryInfo info = new EntryInfo(field);
|
EntryInfo info = new EntryInfo(field, modid);
|
||||||
if ((field.isAnnotationPresent(Entry.class) || field.isAnnotationPresent(Comment.class)) && !field.isAnnotationPresent(Server.class) && !field.isAnnotationPresent(Hidden.class) && PlatformFunctions.isClientEnv())
|
if ((field.isAnnotationPresent(Entry.class) || field.isAnnotationPresent(Comment.class)) && !field.isAnnotationPresent(Server.class) && !field.isAnnotationPresent(Hidden.class) && PlatformFunctions.isClientEnv())
|
||||||
initClient(modid, field, info);
|
initClient(modid, field, info);
|
||||||
if (field.isAnnotationPresent(Entry.class))
|
if (field.isAnnotationPresent(Entry.class))
|
||||||
@@ -122,19 +142,10 @@ public abstract class MidnightConfig {
|
|||||||
}
|
}
|
||||||
loadValuesFromJson(modid);
|
loadValuesFromJson(modid);
|
||||||
}
|
}
|
||||||
@SuppressWarnings("ConstantValue") //pertains to requiredModLoaded
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
private static void initClient(String modid, Field field, EntryInfo info) {
|
private static void initClient(String modid, Field field, EntryInfo info) {
|
||||||
info.dataType = getUnderlyingType(field);
|
Entry e = info.entry;
|
||||||
Entry e = info.entry; Comment c = info.comment;
|
|
||||||
info.modid = modid;
|
|
||||||
boolean requiredModLoaded = true;
|
|
||||||
|
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
if (!e.requiredMod().isEmpty()) requiredModLoaded = PlatformFunctions.isModLoaded(e.requiredMod());
|
|
||||||
|
|
||||||
if (!requiredModLoaded) return;
|
|
||||||
if (!e.name().isEmpty()) info.name = Text.translatable(e.name());
|
|
||||||
if (info.dataType == int.class) textField(info, Integer::parseInt, INTEGER_ONLY, (int) e.min(), (int) e.max(), true);
|
if (info.dataType == int.class) textField(info, Integer::parseInt, INTEGER_ONLY, (int) e.min(), (int) e.max(), true);
|
||||||
else if (info.dataType == float.class) textField(info, Float::parseFloat, DECIMAL_ONLY, (float) e.min(), (float) e.max(), false);
|
else if (info.dataType == float.class) textField(info, Float::parseFloat, DECIMAL_ONLY, (float) e.min(), (float) e.max(), false);
|
||||||
else if (info.dataType == double.class) textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(), false);
|
else if (info.dataType == double.class) textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(), false);
|
||||||
@@ -154,10 +165,9 @@ public abstract class MidnightConfig {
|
|||||||
int index = values.indexOf(info.value) + 1;
|
int index = values.indexOf(info.value) + 1;
|
||||||
info.value = values.get(index >= values.size() ? 0 : index); button.setMessage(func.apply(info.value));
|
info.value = values.get(index >= values.size() ? 0 : index); button.setMessage(func.apply(info.value));
|
||||||
}, func);
|
}, func);
|
||||||
}} else if (c != null) {
|
}
|
||||||
if (!c.requiredMod().isEmpty()) requiredModLoaded = PlatformFunctions.isModLoaded(c.requiredMod());
|
|
||||||
}
|
}
|
||||||
if (requiredModLoaded) entries.add(info);
|
entries.add(info);
|
||||||
}
|
}
|
||||||
public static Class<?> getUnderlyingType(Field field) {
|
public static Class<?> getUnderlyingType(Field field) {
|
||||||
Class<?> rawType = field.getType();
|
Class<?> rawType = field.getType();
|
||||||
@@ -259,12 +269,12 @@ public abstract class MidnightConfig {
|
|||||||
super.tick();
|
super.tick();
|
||||||
if (prevTab != null && prevTab != tabManager.getCurrentTab()) {
|
if (prevTab != null && prevTab != tabManager.getCurrentTab()) {
|
||||||
prevTab = tabManager.getCurrentTab();
|
prevTab = tabManager.getCurrentTab();
|
||||||
this.list.clear(); fillList();
|
updateList(); list.setScrollY(0);
|
||||||
list.setScrollY(0);
|
|
||||||
}
|
}
|
||||||
scrollProgress = list.getScrollY();
|
scrollProgress = list.getScrollY();
|
||||||
for (EntryInfo info : entries) info.updateFieldValue();
|
for (EntryInfo info : entries) info.updateFieldValue();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
if (reloadScreen) { updateList(); reloadScreen = false; }
|
||||||
}
|
}
|
||||||
public void updateButtons() {
|
public void updateButtons() {
|
||||||
if (this.list != null) {
|
if (this.list != null) {
|
||||||
@@ -307,14 +317,18 @@ public abstract class MidnightConfig {
|
|||||||
this.addSelectableChild(this.list); fillList();
|
this.addSelectableChild(this.list); fillList();
|
||||||
if (tabs.size() > 1) list.renderHeaderSeparator = false;
|
if (tabs.size() > 1) list.renderHeaderSeparator = false;
|
||||||
}
|
}
|
||||||
|
public void updateList() {
|
||||||
|
this.list.clear(); fillList();
|
||||||
|
}
|
||||||
public void fillList() {
|
public void fillList() {
|
||||||
for (EntryInfo info : entries) {
|
for (EntryInfo info : entries) {
|
||||||
|
if (!info.conditionsMet && info.condition != null && !info.condition.visibleButLocked()) continue;
|
||||||
if (info.modid.equals(modid) && (info.tab == null || info.tab == tabManager.getCurrentTab())) {
|
if (info.modid.equals(modid) && (info.tab == null || info.tab == tabManager.getCurrentTab())) {
|
||||||
Text name = Objects.requireNonNullElseGet(info.name, () -> Text.translatable(translationPrefix + info.fieldName));
|
Text name = Objects.requireNonNullElseGet(info.name, () -> Text.translatable(translationPrefix + info.fieldName));
|
||||||
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
|
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
|
||||||
info.value = info.defaultValue; info.listIndex = 0;
|
info.value = info.defaultValue; info.listIndex = 0;
|
||||||
info.tempValue = info.toTemporaryValue();
|
info.tempValue = info.toTemporaryValue();
|
||||||
list.clear(); fillList();
|
updateList();
|
||||||
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
|
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
|
||||||
resetButton.setPosition(width - 205 + 150 + 25, 0);
|
resetButton.setPosition(width - 205 + 150 + 25, 0);
|
||||||
|
|
||||||
@@ -348,7 +362,7 @@ public abstract class MidnightConfig {
|
|||||||
if (info.listIndex > values.size()) info.listIndex = 0;
|
if (info.listIndex > values.size()) info.listIndex = 0;
|
||||||
info.tempValue = info.toTemporaryValue();
|
info.tempValue = info.toTemporaryValue();
|
||||||
if (info.listIndex == values.size()) info.tempValue = "";
|
if (info.listIndex == values.size()) info.tempValue = "";
|
||||||
list.clear(); fillList();
|
updateList();
|
||||||
})).dimensions(width - 185, 0, 20, 20).build();
|
})).dimensions(width - 185, 0, 20, 20).build();
|
||||||
}
|
}
|
||||||
if (e.isColor()) {
|
if (e.isColor()) {
|
||||||
@@ -357,7 +371,7 @@ public abstract class MidnightConfig {
|
|||||||
Color newColor = JColorChooser.showDialog(null, Text.translatable("midnightconfig.colorChooser.title").getString(), Color.decode(!Objects.equals(info.tempValue, "") ? info.tempValue : "#FFFFFF"));
|
Color newColor = JColorChooser.showDialog(null, Text.translatable("midnightconfig.colorChooser.title").getString(), Color.decode(!Objects.equals(info.tempValue, "") ? info.tempValue : "#FFFFFF"));
|
||||||
if (newColor != null) {
|
if (newColor != null) {
|
||||||
info.setValue("#" + Integer.toHexString(newColor.getRGB()).substring(2));
|
info.setValue("#" + Integer.toHexString(newColor.getRGB()).substring(2));
|
||||||
list.clear(); fillList();
|
updateList();
|
||||||
}
|
}
|
||||||
}).start()
|
}).start()
|
||||||
).dimensions(width - 185, 0, 20, 20).build();
|
).dimensions(width - 185, 0, 20, 20).build();
|
||||||
@@ -375,13 +389,14 @@ public abstract class MidnightConfig {
|
|||||||
Text.translatable(translationPrefix + info.fieldName + ".fileFilter").getString(), e.fileExtensions()));
|
Text.translatable(translationPrefix + info.fieldName + ".fileFilter").getString(), e.fileExtensions()));
|
||||||
if (fileChooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION) {
|
if (fileChooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION) {
|
||||||
info.setValue(fileChooser.getSelectedFile().getAbsolutePath());
|
info.setValue(fileChooser.getSelectedFile().getAbsolutePath());
|
||||||
list.clear(); fillList();
|
updateList();
|
||||||
}
|
}
|
||||||
}).start(), true
|
}).start(), true
|
||||||
).texture(Identifier.of("midnightlib", "icon/explorer"), 12, 12).dimension(20, 20).build();
|
).texture(Identifier.of("midnightlib", "icon/explorer"), 12, 12).dimension(20, 20).build();
|
||||||
explorerButton.setPosition(width - 185, 0);
|
explorerButton.setPosition(width - 185, 0);
|
||||||
info.actionButton = explorerButton;
|
info.actionButton = explorerButton;
|
||||||
}
|
}
|
||||||
|
if (!info.conditionsMet) widget.active = false;
|
||||||
List<ClickableWidget> widgets = Lists.newArrayList(widget, resetButton);
|
List<ClickableWidget> widgets = Lists.newArrayList(widget, resetButton);
|
||||||
if (info.actionButton != null) {
|
if (info.actionButton != null) {
|
||||||
if (IS_SYSTEM_MAC) info.actionButton.active = false;
|
if (IS_SYSTEM_MAC) info.actionButton.active = false;
|
||||||
@@ -516,7 +531,7 @@ public abstract class MidnightConfig {
|
|||||||
boolean isSlider() default false;
|
boolean isSlider() default false;
|
||||||
int precision() default 100;
|
int precision() default 100;
|
||||||
String category() default "default";
|
String category() default "default";
|
||||||
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 {}
|
||||||
@@ -536,6 +551,25 @@ public abstract class MidnightConfig {
|
|||||||
@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 requiredMod() default "";
|
String name() default "";
|
||||||
|
@Deprecated String requiredMod() default "";
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Condition Annotation<br>
|
||||||
|
* - <b>{@link Condition#requiredModId()}</b>: The id of a mod that is required to be loaded.<br>
|
||||||
|
* - <b>{@link Condition#requiredOption()}</b>: The {@link Field} which will be used to check the condition. Can also access options of other MidnightLib mods ("modid:optionName").<br>
|
||||||
|
* - <b>{@link Condition#requiredValue()}</b>: The value that {@link Condition#requiredOption()} should be set to for the condition to be met.<br>
|
||||||
|
* - <b>{@link Condition#visibleButLocked()}</b>: The behaviour to take when {@link Condition#requiredModId} is not loaded
|
||||||
|
* or {@link Condition#requiredOption()} returns a value that is not {@link Condition#requiredValue()}.<br>
|
||||||
|
* <code>true</code> – Option is visible, but not editable<br>
|
||||||
|
* <code>false</code> – Option is completely hidden
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Condition {
|
||||||
|
String requiredModId() default "";
|
||||||
|
String requiredOption() default "";
|
||||||
|
String requiredValue() default "true";
|
||||||
|
boolean visibleButLocked() default false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@ public class MidnightConfigExample extends MidnightConfig {
|
|||||||
public static final String SLIDERS = "sliders";
|
public static final String SLIDERS = "sliders";
|
||||||
public static final String LISTS = "lists";
|
public static final String LISTS = "lists";
|
||||||
public static final String FILES = "files";
|
public static final String FILES = "files";
|
||||||
|
public static final String CONDITIONS = "conditions";
|
||||||
|
|
||||||
@Comment(category = TEXT) public static Comment text1; // Comments are rendered like an option without a button and are excluded from the config file
|
@Comment(category = TEXT) public static Comment text1; // Comments are rendered like an option without a button and are excluded from the config file
|
||||||
@Comment(category = TEXT, centered = true) public static Comment text2; // Centered comments are the same as normal ones - just centered!
|
@Comment(category = TEXT, centered = true) public static Comment text2; // Centered comments are the same as normal ones - just centered!
|
||||||
@@ -69,5 +70,32 @@ public class MidnightConfigExample extends MidnightConfig {
|
|||||||
name = "I am a mf file/directory list!")
|
name = "I am a mf file/directory list!")
|
||||||
public static List<String> fileOrDirectoryList = new ArrayList<>(); // Yes, that's right – you can even have lists of files/directories
|
public static List<String> fileOrDirectoryList = new ArrayList<>(); // Yes, that's right – you can even have lists of files/directories
|
||||||
|
|
||||||
|
@Condition(requiredModId = "midnightlib") // Conditional options are here!
|
||||||
|
@Entry(category = CONDITIONS, name="Turn me on!")
|
||||||
|
public static boolean turnMeOn = false;
|
||||||
|
@Condition(requiredOption = "modid:turnMeOn", visibleButLocked = true)
|
||||||
|
@Entry(category = CONDITIONS, name="Turn me off!")
|
||||||
|
public static Boolean turnMeOff = true;
|
||||||
|
@Condition(requiredOption = "modid:turnMeOff", requiredValue = "false")
|
||||||
|
@Entry(category = CONDITIONS, name="Which is the best modloader?")
|
||||||
|
public static String bestModloader = "";
|
||||||
|
@Condition(requiredOption = "bestModloader", requiredValue = "Forge")
|
||||||
|
@Comment(category = CONDITIONS, name="❌ You have bad taste :(", centered = true) // Don't take this too seriously btw :)
|
||||||
|
public static Comment answerForge; // Comments can also be conditional!
|
||||||
|
@Condition(requiredOption = "bestModloader", requiredValue = "NeoForge")
|
||||||
|
@Comment(category = CONDITIONS, name="⛏ Not quite, but it's alright!", centered = true)
|
||||||
|
public static Comment answerNeoforge;
|
||||||
|
@Condition(requiredOption = "bestModloader", requiredValue = "Fabric")
|
||||||
|
@Comment(category = CONDITIONS, name="⭐ Correct! Fabric (and Quilt) are the best!", centered = true)
|
||||||
|
public static Comment answerFabric;
|
||||||
|
@Condition(requiredOption = "bestModloader", requiredValue = "Quilt")
|
||||||
|
@Comment(category = CONDITIONS, name="⭐ Correct! Quilt (and Fabric) are the best!", centered = true)
|
||||||
|
public static Comment answerQuilt;
|
||||||
|
|
||||||
|
@Condition(requiredOption = "midnightlib:config_screen_list", requiredValue = "FALSE") // Access options of other mods that are also using MidnightLib
|
||||||
|
@Comment(category = CONDITIONS) public static Comment spaceracer;
|
||||||
|
@Condition(requiredOption = "midnightlib:config_screen_list", requiredValue = "FALSE")
|
||||||
|
@Comment(category = CONDITIONS, name="You disabled MidnightLib's config screen list. Why? :(", centered = true) public static Comment why;
|
||||||
|
|
||||||
public static int imposter = 16777215; // - Entries without an @Entry or @Comment annotation are ignored
|
public static int imposter = 16777215; // - Entries without an @Entry or @Comment annotation are ignored
|
||||||
}
|
}
|
||||||
@@ -20,5 +20,6 @@
|
|||||||
"modid.midnightconfig.category.text": "Text",
|
"modid.midnightconfig.category.text": "Text",
|
||||||
"modid.midnightconfig.category.sliders": "Sliders",
|
"modid.midnightconfig.category.sliders": "Sliders",
|
||||||
"modid.midnightconfig.category.lists": "Lists",
|
"modid.midnightconfig.category.lists": "Lists",
|
||||||
"modid.midnightconfig.category.files": "Files"
|
"modid.midnightconfig.category.files": "Files",
|
||||||
|
"modid.midnightconfig.category.conditions": "Quiz"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user