Merge branch 'architectury-1.21.4' into architectury-1.21.4

This commit is contained in:
Martin Prokoph
2025-05-12 22:46:51 +02:00
committed by GitHub
4 changed files with 136 additions and 16 deletions

View File

@@ -14,6 +14,7 @@ import net.minecraft.client.resource.language.I18n;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.screen.ScreenTexts; import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Style; import net.minecraft.text.Text; 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.Formatting; import net.minecraft.util.Identifier;
import net.minecraft.util.TranslatableOption; import net.minecraft.util.TranslatableOption;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -107,6 +108,10 @@ public abstract class MidnightConfig {
if (index >= list.size()) list.add(value); if (index >= list.size()) list.add(value);
else list.set(index, 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, Class<? extends MidnightConfig>> configClass = new HashMap<>();
@@ -120,11 +125,6 @@ public abstract class MidnightConfig {
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();
@SuppressWarnings("unused") // Utility for mod authors
public static @Nullable Object getDefaultValue(String modid, String entry) {
String key = modid + ":" + entry;
return entries.containsKey(key) ? entries.get(key).defaultValue : null;
}
public static void loadValuesFromJson(String modid) { public static void loadValuesFromJson(String modid) {
try { gson.fromJson(Files.newBufferedReader(path), configClass.get(modid)); } try { gson.fromJson(Files.newBufferedReader(path), configClass.get(modid)); }
catch (Exception e) { write(modid); } catch (Exception e) { write(modid); }
@@ -188,10 +188,6 @@ public abstract class MidnightConfig {
try { return (Class<?>) rawType.getField("TYPE").get(null); // Tries to get primitive types from non-primitives (e.g. Boolean -> boolean) try { return (Class<?>) rawType.getField("TYPE").get(null); // Tries to get primitive types from non-primitives (e.g. Boolean -> boolean)
} catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; } } catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; }
} }
public static Tooltip getTooltip(EntryInfo info, boolean isButton) {
String key = info.modid + ".midnightconfig."+info.fieldName+(!isButton ? ".label" : "" )+".tooltip";
return Tooltip.of(isButton && info.error != null ? info.error : I18n.hasTranslation(key) ? Text.translatable(key) : Text.empty());
}
private static Text getEnumTranslatableText(Object value, String modid, EntryInfo info) { private static Text getEnumTranslatableText(Object value, String modid, EntryInfo info) {
if (value instanceof TranslatableOption translatableOption) { if (value instanceof TranslatableOption translatableOption) {
@@ -216,7 +212,7 @@ public abstract class MidnightConfig {
info.error = inLimits? null : Text.literal(value.doubleValue() < min ? info.error = inLimits? null : Text.literal(value.doubleValue() < min ?
"§cMinimum " + (isNumber? "value" : "length") + (cast? " is " + (int)min : " is " + min) : "§cMinimum " + (isNumber? "value" : "length") + (cast? " is " + (int)min : " is " + min) :
"§cMaximum " + (isNumber? "value" : "length") + (cast? " is " + (int)max : " is " + max)).formatted(Formatting.RED); "§cMaximum " + (isNumber? "value" : "length") + (cast? " is " + (int)max : " is " + max)).formatted(Formatting.RED);
t.setTooltip(getTooltip(info, true)); t.setTooltip(info.getTooltip(true));
} }
info.tempValue = s; info.tempValue = s;
@@ -248,6 +244,15 @@ public abstract class MidnightConfig {
Files.write(path, gson.toJson(getClass(modid)).getBytes()); Files.write(path, gson.toJson(getClass(modid)).getBytes());
} catch (Exception e) { e.fillInStackTrace(); } } catch (Exception e) { e.fillInStackTrace(); }
} }
@SuppressWarnings("unused") // Utility for mod authors
public static @Nullable Object getDefaultValue(String modid, String entry) {
String key = modid + ":" + entry;
return entries.containsKey(key) ? entries.get(key).defaultValue : null;
}
public void onTabInit(String tabName, MidnightConfigListWidget list, MidnightConfigScreen screen) {}
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public static Screen getScreen(Screen parent, String modid) { public static Screen getScreen(Screen parent, String modid) {
return new MidnightConfigScreen(parent, modid); return new MidnightConfigScreen(parent, modid);
@@ -302,9 +307,9 @@ public abstract class MidnightConfig {
public void updateButtons() { public void updateButtons() {
if (this.list != null) { if (this.list != null) {
for (ButtonEntry entry : this.list.children()) { for (ButtonEntry entry : this.list.children()) {
if (entry.buttons != null && entry.buttons.size() > 1) { if (entry.buttons != null && entry.buttons.size() > 1 && entry.info.field != null) {
if (entry.buttons.get(0) instanceof ClickableWidget widget) if (entry.buttons.get(0) instanceof ClickableWidget widget)
if (widget.isFocused() || widget.isHovered()) widget.setTooltip(getTooltip(entry.info, true)); if (widget.isFocused() || widget.isHovered()) widget.setTooltip(entry.info.getTooltip(true));
if (entry.buttons.get(1) instanceof ButtonWidget button) 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; button.active = !Objects.equals(String.valueOf(entry.info.value), String.valueOf(entry.info.defaultValue)) && entry.info.conditionsMet;
}}}} }}}}
@@ -344,7 +349,9 @@ public abstract class MidnightConfig {
this.list.clear(); fillList(); this.list.clear(); fillList();
} }
public void fillList() { public void fillList() {
MidnightConfig.getClass(modid).onTabInit(prevTab.getTitle().getContent() instanceof TranslatableTextContent translatable ? translatable.getKey().replace("%s.midnightconfig.category.".formatted(modid), "") : prevTab.getTitle().toString(), list, this);
for (EntryInfo info : entries.values()) { for (EntryInfo info : entries.values()) {
info.updateConditions();
if (!info.conditionsMet) { if (!info.conditionsMet) {
boolean visibleButLocked = false; boolean visibleButLocked = false;
for (Condition condition : info.conditions) { for (Condition condition : info.conditions) {
@@ -369,7 +376,7 @@ public abstract class MidnightConfig {
if (info.dataType.isEnum()) { if (info.dataType.isEnum()) {
values.setValue(value -> getEnumTranslatableText(value, modid, info)); values.setValue(value -> getEnumTranslatableText(value, modid, info));
} }
widget = ButtonWidget.builder(values.getValue().apply(info.value), values.getKey()).dimensions(width - 185, 0, 150, 20).tooltip(getTooltip(info, true)).build(); widget = ButtonWidget.builder(values.getValue().apply(info.value), values.getKey()).dimensions(width - 185, 0, 150, 20).tooltip(info.getTooltip(true)).build();
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(); 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()) } 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); widget = new MidnightSliderWidget(width - 185, 0, 150, 20, Text.of(info.tempValue), (Double.parseDouble(info.tempValue) - e.min()) / (e.max() - e.min()), info);
@@ -379,7 +386,7 @@ public abstract class MidnightConfig {
Predicate<String> processor = ((BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) info.function).apply(textField, done); Predicate<String> processor = ((BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) info.function).apply(textField, done);
textField.setTextPredicate(processor); textField.setTextPredicate(processor);
} }
widget.setTooltip(getTooltip(info, true)); widget.setTooltip(info.getTooltip(true));
ButtonWidget cycleButton = null; ButtonWidget cycleButton = null;
if (info.field.getType() == List.class) { if (info.field.getType() == List.class) {
@@ -478,14 +485,14 @@ public abstract class MidnightConfig {
if (text != null && (!text.getString().contains("spacer") || !buttons.isEmpty())) { 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 = new MultilineTextWidget((centered) ? (scaledWidth / 2 - (textRenderer.getWidth(text) / 2)) : 12, 0, Text.of(text), textRenderer);
if (info != null) title.setTooltip(getTooltip(info, false)); if (info != null) title.setTooltip(info.getTooltip(false));
title.setMaxWidth(buttons.size() > 1 ? buttons.get(1).getX() - 24 : scaledWidth - 24); title.setMaxWidth(buttons.size() > 1 ? buttons.get(1).getX() - 24 : scaledWidth - 24);
} }
} }
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { 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 instanceof CheckboxWidget ? 1 : 0)); 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) { if (title != null) {
title.setY(y + 9); title.setY(y+5);
title.renderWidget(context, mouseX, mouseY, tickDelta); title.renderWidget(context, mouseX, mouseY, tickDelta);
boolean tooltipVisible = mouseX >= title.getX() && mouseX < title.getWidth() + title.getX() && mouseY >= title.getY() && mouseY < title.getHeight() + title.getY(); boolean tooltipVisible = mouseX >= title.getX() && mouseX < title.getWidth() + title.getX() && mouseY >= title.getY() && mouseY < title.getHeight() + title.getY();

View File

@@ -0,0 +1,96 @@
package eu.midnightdust.fabric.example;
import com.google.common.collect.Lists;
import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
/*
Pre-made additional (niche) functionality that is not included in MidnightLib to keep the file size small.
Feel free to copy the parts you need :)
*/
public class MidnightLibExtras {
public static class KeybindButton extends ButtonWidget {
public static ButtonWidget focusedButton;
public static void add(KeyBinding binding, MidnightConfig.MidnightConfigListWidget list, MidnightConfig.MidnightConfigScreen screen) {
KeybindButton editButton = new KeybindButton(screen.width - 185, 0, 150, 20, binding);
TextIconButtonWidget resetButton = TextIconButtonWidget.builder(Text.translatable("controls.reset"), (button -> {
binding.setBoundKey(binding.getDefaultKey());
screen.updateList();
}), true).texture(Identifier.of("midnightlib","icon/reset"), 12, 12).dimension(20, 20).build();
resetButton.setPosition(screen.width - 205 + 150 + 25, 0);
editButton.resetButton = resetButton;
editButton.updateMessage(false);
MidnightConfig.EntryInfo info = new MidnightConfig.EntryInfo(null, screen.modid);
list.addButton(Lists.newArrayList(editButton, resetButton), Text.translatable(binding.getTranslationKey()), info);
}
private final KeyBinding binding;
private @Nullable ClickableWidget resetButton;
public KeybindButton(int x, int y, int width, int height, KeyBinding binding) {
super(x, y, width, height, binding.getBoundKeyLocalizedText(), (button) -> {
((KeybindButton) button).updateMessage(true);
focusedButton = button;
}, (textSupplier) -> binding.isUnbound() ? Text.translatable("narrator.controls.unbound", binding.getTranslationKey()) : Text.translatable("narrator.controls.bound", binding.getTranslationKey(), textSupplier.get()));
this.binding = binding;
updateMessage(false);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (focusedButton == this) {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
this.binding.setBoundKey(InputUtil.UNKNOWN_KEY);
} else {
this.binding.setBoundKey(InputUtil.fromKeyCode(keyCode, scanCode));
}
updateMessage(false);
focusedButton = null;
return true;
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
public void updateMessage(boolean focused) {
boolean hasConflicts = false;
MutableText conflictingBindings = Text.empty();
if (focused) this.setMessage(Text.literal("> ").append(this.binding.getBoundKeyLocalizedText().copy().formatted(Formatting.WHITE, Formatting.UNDERLINE)).append(" <").formatted(Formatting.YELLOW));
else {
this.setMessage(this.binding.getBoundKeyLocalizedText());
if (!this.binding.isUnbound()) {
for(KeyBinding keyBinding : MinecraftClient.getInstance().options.allKeys) {
if (keyBinding != this.binding && this.binding.equals(keyBinding)) {
if (hasConflicts) conflictingBindings.append(", ");
hasConflicts = true;
conflictingBindings.append(Text.translatable(keyBinding.getTranslationKey()));
}
}
}
}
if (this.resetButton != null) this.resetButton.active = !this.binding.isDefault();
if (hasConflicts) {
this.setMessage(Text.literal("[ ").append(this.getMessage().copy().formatted(Formatting.WHITE)).append(" ]").formatted(Formatting.RED));
this.setTooltip(Tooltip.of(Text.translatable("controls.keybinds.duplicateKeybinds", conflictingBindings)));
} else {
this.setTooltip(null);
}
}
}
}

View File

@@ -1,16 +1,19 @@
package eu.midnightdust.fabric.example.config; package eu.midnightdust.fabric.example.config;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import eu.midnightdust.fabric.example.MidnightLibExtras;
import eu.midnightdust.lib.config.MidnightConfig; import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.TranslatableOption; import net.minecraft.util.TranslatableOption;
import javax.swing.*; import javax.swing.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
/** Every option in a MidnightConfig class has to be public and static, so we can access it from other classes. /** 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*/ * The config class also has to extend MidnightConfig*/
@@ -22,6 +25,7 @@ public class MidnightConfigExample extends MidnightConfig {
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"; public static final String CONDITIONS = "conditions";
public static final String EXTRAS = "extras";
@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!
@@ -150,4 +154,16 @@ public class MidnightConfigExample extends MidnightConfig {
@Comment(category = CONDITIONS, name="You disabled MidnightLib's config screen list. Why? :(", centered = true) public static Comment why; @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
@Condition(requiredModId = "thismoddoesnotexist")
@Comment(category = EXTRAS) public static Comment iAmJustADummy; // We only have this to initialize an empty tab for the keybinds below
@Override
public void onTabInit(String tabName, MidnightConfigListWidget list, MidnightConfigScreen screen) {
if (Objects.equals(tabName, EXTRAS)) {
MidnightLibExtras.KeybindButton.add(MinecraftClient.getInstance().options.advancementsKey, list, screen);
MidnightLibExtras.KeybindButton.add(MinecraftClient.getInstance().options.dropKey, list, screen);
}
}
} }

View File

@@ -25,5 +25,6 @@
"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", "modid.midnightconfig.category.conditions": "Quiz",
"modid.midnightconfig.category.extras": "Extras",
"modid.midnightconfig.category.multiConditions": "Multi-Conditions" "modid.midnightconfig.category.multiConditions": "Multi-Conditions"
} }