mirror of
https://github.com/TeamMidnightDust/MidnightLib.git
synced 2025-12-15 09:05:08 +01:00
Merge pull request #99 from TeamMidnightDust/dev
MidnightLib 1.7.2 – Clickable URLs in Comments and added Checkbox buttons
This commit is contained in:
@@ -5,7 +5,8 @@ import com.google.gson.*; import com.google.gson.stream.*;
|
||||
import eu.midnightdust.lib.util.PlatformFunctions;
|
||||
import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.Element; import net.minecraft.client.gui.Selectable; import net.minecraft.client.gui.screen.Screen;
|
||||
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.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.render.RenderLayer;
|
||||
@@ -66,9 +67,7 @@ public abstract class MidnightConfig {
|
||||
this.entry = field.getAnnotation(Entry.class);
|
||||
this.comment = field.getAnnotation(Comment.class);
|
||||
this.conditions = field.getAnnotationsByType(Condition.class);
|
||||
} else {
|
||||
this.fieldName = ""; this.dataType = null;
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
@@ -97,7 +96,7 @@ public abstract class MidnightConfig {
|
||||
this.conditionsMet = false;
|
||||
String requiredOption = condition.requiredOption().contains(":") ? condition.requiredOption() : (this.modid + ":" + condition.requiredOption());
|
||||
if (entries.get(requiredOption) instanceof EntryInfo info)
|
||||
this.conditionsMet &= condition.requiredValue().equals(info.tempValue);
|
||||
this.conditionsMet &= List.of(condition.requiredValue()).contains(info.tempValue);
|
||||
if (!this.conditionsMet) break;
|
||||
}
|
||||
if (prevConditionState != this.conditionsMet) reloadScreen = true;
|
||||
@@ -164,6 +163,9 @@ public abstract class MidnightConfig {
|
||||
else if (info.dataType == boolean.class) {
|
||||
Function<Object, Text> func = value -> Text.translatable((Boolean) value ? "gui.yes" : "gui.no").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED);
|
||||
info.function = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object, Text>>(button -> {
|
||||
if (info.actionButton instanceof CheckboxWidget checkbox && checkbox.isChecked() == (Boolean) info.value) {
|
||||
checkbox.onPress(); return;
|
||||
}
|
||||
info.setValue(!(Boolean) info.value); button.setMessage(func.apply(info.value));
|
||||
}, func);
|
||||
} else if (info.dataType.isEnum()) {
|
||||
@@ -197,7 +199,8 @@ public abstract class MidnightConfig {
|
||||
boolean isNumber = pattern != null;
|
||||
info.function = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
|
||||
s = s.trim();
|
||||
if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches())) return false;
|
||||
if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches()) ||
|
||||
(info.dataType == Identifier.class && Identifier.validate(s).isError())) return false;
|
||||
|
||||
Number value = 0; boolean inLimits = false; info.error = null;
|
||||
if (!(isNumber && s.isEmpty()) && !s.equals("-") && !s.equals(".")) {
|
||||
@@ -296,7 +299,7 @@ public abstract class MidnightConfig {
|
||||
if (entry.buttons.get(0) instanceof ClickableWidget widget)
|
||||
if (widget.isFocused() || widget.isHovered()) widget.setTooltip(getTooltip(entry.info, true));
|
||||
if (entry.buttons.get(1) instanceof ButtonWidget button)
|
||||
button.active = !Objects.equals(entry.info.value.toString(), entry.info.defaultValue.toString());
|
||||
button.active = !Objects.equals(String.valueOf(entry.info.value), String.valueOf(entry.info.defaultValue)) && entry.info.conditionsMet;
|
||||
}}}}
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
@@ -354,17 +357,15 @@ public abstract class MidnightConfig {
|
||||
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 -> Text.translatable(translationPrefix + "enum." + info.dataType.getSimpleName() + "." + info.value.toString()));
|
||||
widget = ButtonWidget.builder(values.getValue().apply(info.value), values.getKey()).dimensions(width - 185, 0, 150, 20).tooltip(getTooltip(info, true)).build();
|
||||
}
|
||||
else if (e.isSlider())
|
||||
if (info.dataType == boolean.class) info.actionButton = CheckboxWidget.builder(Text.empty(), textRenderer).callback((checkbox, checked) -> values.getKey().onPress((ButtonWidget) widget)).checked((Boolean) info.value).pos(widget.getX(), 1).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);
|
||||
@@ -415,7 +416,6 @@ public abstract class MidnightConfig {
|
||||
explorerButton.setPosition(width - 185, 0);
|
||||
info.actionButton = explorerButton;
|
||||
}
|
||||
if (!info.conditionsMet) widget.active = false;
|
||||
List<ClickableWidget> widgets = Lists.newArrayList(widget, resetButton);
|
||||
if (info.actionButton != null) {
|
||||
if (IS_SYSTEM_MAC) info.actionButton.active = false;
|
||||
@@ -426,6 +426,7 @@ public abstract class MidnightConfig {
|
||||
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);
|
||||
@@ -474,7 +475,7 @@ public abstract class MidnightConfig {
|
||||
}
|
||||
}
|
||||
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||
buttons.forEach(b -> { b.setY(y); b.render(context, mouseX, mouseY, tickDelta);});
|
||||
buttons.forEach(b -> { b.setY(y + (b instanceof CheckboxWidget ? 1 : 0)); b.render(context, mouseX, mouseY, tickDelta);});
|
||||
if (title != null) {
|
||||
title.setY(y + 9);
|
||||
title.renderWidget(context, mouseX, mouseY, tickDelta);
|
||||
@@ -488,6 +489,14 @@ public abstract class MidnightConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
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(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
public List<? extends Element> children() {return Lists.newArrayList(buttons);}
|
||||
public List<? extends Selectable> selectableChildren() {return Lists.newArrayList(buttons);}
|
||||
}
|
||||
@@ -519,7 +528,7 @@ public abstract class MidnightConfig {
|
||||
* - <b>width</b>: The maximum character length of the {@link String}, {@link Identifier} or String/Identifier {@link List<>} field<br>
|
||||
* - <b>min</b>: The minimum value of the <code>int</code>, <code>float</code> or <code>double</code> field<br>
|
||||
* - <b>max</b>: The maximum value of the <code>int</code>, <code>float</code> or <code>double</code> field<br>
|
||||
* - <b>name</b>: The name of the field in the config screen<br>
|
||||
* - <b>name</b>: Will be used instead of the default translation key, if not empty<br>
|
||||
* - <b>selectionMode</b>: The selection mode of the file picker button for {@link String} fields,
|
||||
* -1 for none, {@link JFileChooser#FILES_ONLY} for files, {@link JFileChooser#DIRECTORIES_ONLY} for directories,
|
||||
* {@link JFileChooser#FILES_AND_DIRECTORIES} for both (default: -1). Remember to set the translation key
|
||||
@@ -567,10 +576,18 @@ public abstract class MidnightConfig {
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Hidden {}
|
||||
|
||||
/**
|
||||
* Comment Annotation<br>
|
||||
* - <b>{@link Comment#centered()}</b>: If the comment should be centered<br>
|
||||
* - <b>{@link Comment#category()}</b>: The category of the comment in the config screen<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>
|
||||
* */
|
||||
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Comment {
|
||||
boolean centered() default false;
|
||||
String category() default "default";
|
||||
String name() default "";
|
||||
String url() default "";
|
||||
@Deprecated String requiredMod() default "";
|
||||
}
|
||||
/**
|
||||
@@ -589,7 +606,7 @@ public abstract class MidnightConfig {
|
||||
public @interface Condition {
|
||||
String requiredModId() default "";
|
||||
String requiredOption() default "";
|
||||
String requiredValue() default "true";
|
||||
String[] requiredValue() default {"true"};
|
||||
boolean visibleButLocked() default false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ yarn_mappings=1.21.4+build.1
|
||||
enabled_platforms=fabric,neoforge
|
||||
|
||||
archives_base_name=midnightlib
|
||||
mod_version=1.7.1
|
||||
mod_version=1.7.2
|
||||
maven_group=eu.midnightdust
|
||||
release_type=release
|
||||
curseforge_id=488090
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
|
||||
/** Every option in a MidnightConfig class has to be public and static, so we can access it from other classes.
|
||||
* The config class also has to extend MidnightConfig*/
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({"unused", "DefaultAnnotationParam"})
|
||||
public class MidnightConfigExample extends MidnightConfig {
|
||||
public static final String TEXT = "text";
|
||||
public static final String NUMBERS = "numbers";
|
||||
@@ -31,6 +31,8 @@ public class MidnightConfigExample extends MidnightConfig {
|
||||
public enum ModPlatform { // Enums allow the user to cycle through predefined options
|
||||
QUILT, FABRIC, FORGE, NEOFORGE, VANILLA
|
||||
}
|
||||
@Comment(category = TEXT, name = "§nMidnightLib Wiki", centered = true, url = "https://www.midnightdust.eu/wiki/midnightlib/") public static Comment wiki; // Example for a comment with a url
|
||||
|
||||
@Entry(category = NUMBERS) public static int fabric = 16777215; // Example for an int option
|
||||
@Entry(category = NUMBERS) public static double world = 1.4D; // Example for a double option
|
||||
@Entry(category = NUMBERS, min=69,max=420) public static int hello = 420; // - The entered number has to be larger than 69 and smaller than 420
|
||||
@@ -74,7 +76,7 @@ public class MidnightConfigExample extends MidnightConfig {
|
||||
@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 (locked if modid:turnMeOn is false)!")
|
||||
@Entry(category = CONDITIONS, name="Turn me off (locked)!")
|
||||
public static Boolean turnMeOff = true;
|
||||
@Condition(requiredOption = "turnMeOn") // You can also use multiple conditions for the same entry
|
||||
@Condition(requiredOption = "modid:turnMeOff", requiredValue = "false")
|
||||
@@ -101,6 +103,12 @@ public class MidnightConfigExample extends MidnightConfig {
|
||||
@Comment(category = CONDITIONS, name="⭐ Correct! Quilt (and Fabric) are the best!", centered = true)
|
||||
public static Comment answerQuilt;
|
||||
|
||||
@Entry(category = CONDITIONS, name="Enter any prime number below 10")
|
||||
public static int primeNumber = 0;
|
||||
@Comment(category = CONDITIONS, name="Correct!")
|
||||
@Condition(requiredOption = "primeNumber", requiredValue = {"2", "3", "5", "7"})
|
||||
public static Comment answerPrime;
|
||||
|
||||
@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")
|
||||
|
||||
@@ -11,13 +11,6 @@
|
||||
"modid.midnightconfig.hello":"I am a limited int!",
|
||||
"modid.midnightconfig.id":"I am an Item Identifier!",
|
||||
"modid.midnightconfig.modPlatform":"I am an enum!",
|
||||
"modid.midnightconfig.enum.Arch.X86":"X86",
|
||||
"modid.midnightconfig.enum.Arch.X86_64":"X86_64",
|
||||
"modid.midnightconfig.enum.Arch.AARCH64":"AARCH64",
|
||||
"modid.midnightconfig.enum.Arch.RISCV64":"RISCV64",
|
||||
"modid.midnightconfig.enum.OS.LINUX":"Linux",
|
||||
"modid.midnightconfig.enum.OS.WINDOWS":"Windows",
|
||||
"modid.midnightconfig.enum.OS.MAC":"MacOS",
|
||||
"modid.midnightconfig.enum.ModPlatform.FORGE":"Forge",
|
||||
"modid.midnightconfig.enum.ModPlatform.FABRIC":"Fabric",
|
||||
"modid.midnightconfig.enum.ModPlatform.QUILT":"Quilt",
|
||||
|
||||
Reference in New Issue
Block a user