From e640fe51452a5a9df6770a7be7ca421b8c6e5638 Mon Sep 17 00:00:00 2001 From: JFronny Date: Sat, 11 Mar 2023 21:21:53 +0100 Subject: [PATCH] [config-ui-tiny] use tabs if upper level contains nothing of note --- gradle.properties | 6 +- .../config/api/v1/ui/tiny/WidgetFactory.java | 4 +- .../config/impl/ui/tiny/Placeholder.java | 121 ++++++++++++++ .../config/impl/ui/tiny/TinyConfigScreen.java | 147 ++++++++++-------- .../config/impl/ui/tiny/TinyConfigTab.java | 99 ++++++++++++ .../impl/ui/tiny/TinyConfigTabWrapper.java | 36 +++++ .../config/impl/ui/tiny/WidgetState.java | 11 +- .../ui/tiny/entry/EntryInfoWidgetBuilder.java | 43 +++-- .../impl/ui/tiny/entry/EntryListWidget.java | 56 ++++++- .../resources/assets/libjf/icon.png | Bin 10 files changed, 420 insertions(+), 103 deletions(-) create mode 100644 libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/Placeholder.java create mode 100644 libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTab.java create mode 100644 libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTabWrapper.java rename src/{main => client}/resources/assets/libjf/icon.png (100%) diff --git a/gradle.properties b/gradle.properties index f565842..7fc8beb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # https://fabricmc.net/develop/ -minecraft_version=1.19.4-pre1 -yarn_mappings=build.6 +minecraft_version=1.19.4-rc2 +yarn_mappings=build.1 loader_version=0.14.17 maven_group=io.gitlab.jfronny.libjf @@ -14,7 +14,7 @@ modrinth_optional_dependencies=fabric-api curseforge_id=482600 curseforge_optional_dependencies=fabric-api -fabric_version=0.75.1+1.19.4 +fabric_version=0.75.3+1.19.4 commons_version=1.1-SNAPSHOT gson_compile_version=1.2-SNAPSHOT modmenu_version=6.1.0-beta.3 diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java index 8963f74..519918e 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java @@ -1,11 +1,11 @@ package io.gitlab.jfronny.libjf.config.api.v1.ui.tiny; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.TinyConfigScreen; import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ClickableWidget; public interface WidgetFactory { - Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done); + Widget build(TinyConfigScreen screen, TextRenderer textRenderer); record Widget(Runnable updateControls, ClickableWidget control) { } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/Placeholder.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/Placeholder.java new file mode 100644 index 0000000..b001a25 --- /dev/null +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/Placeholder.java @@ -0,0 +1,121 @@ +package io.gitlab.jfronny.libjf.config.impl.ui.tiny; + +import net.minecraft.client.gui.*; +import net.minecraft.client.gui.navigation.GuiNavigation; +import net.minecraft.client.gui.navigation.GuiNavigationPath; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.util.math.MatrixStack; +import org.jetbrains.annotations.Nullable; + +public final class Placeholder implements Element, Selectable, Drawable { + private T child; + + public Placeholder(T child) { + this.child = child; + } + + public void setChild(T child) { + this.child = child; + } + + public T getChild() { + return child; + } + + @Override + public SelectionType getType() { + return child.getType(); + } + + @Override + public boolean isNarratable() { + return child.isNarratable(); + } + + @Override + public void appendNarrations(NarrationMessageBuilder builder) { + child.appendNarrations(builder); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + child.render(matrices, mouseX, mouseY, delta); + } + + @Override + public void mouseMoved(double mouseX, double mouseY) { + child.mouseMoved(mouseX, mouseY); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return child.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + return child.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + return child.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + return child.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return child.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + return child.keyReleased(keyCode, scanCode, modifiers); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + return child.charTyped(chr, modifiers); + } + + @Nullable + @Override + public GuiNavigationPath getNavigationPath(GuiNavigation navigation) { + return child.getNavigationPath(navigation); + } + + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + return child.isMouseOver(mouseX, mouseY); + } + + @Override + public void setFocused(boolean focused) { + child.setFocused(focused); + } + + @Override + public boolean isFocused() { + return child.isFocused(); + } + + @Nullable + @Override + public GuiNavigationPath getFocusedPath() { + return child.getFocusedPath(); + } + + @Override + public ScreenRect getNavigationFocus() { + return child.getNavigationFocus(); + } + + @Override + public int getNavigationOrder() { + return child.getNavigationOrder(); + } +} diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java index cd6ae4f..c9804b8 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java @@ -3,16 +3,17 @@ package io.gitlab.jfronny.libjf.config.impl.ui.tiny; import io.gitlab.jfronny.commons.throwable.Try; import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; -import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder; import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; -import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.EntryInfoWidgetBuilder; import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.EntryListWidget; import io.gitlab.jfronny.libjf.config.impl.ui.tiny.presets.PresetsScreen; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tab.TabManager; import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TabNavigationWidget; import net.minecraft.client.resource.language.I18n; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.screen.ScreenTexts; @@ -23,13 +24,6 @@ import java.util.*; @Environment(EnvType.CLIENT) public class TinyConfigScreen extends Screen { - public TinyConfigScreen(ConfigCategory config, Screen parent) { - super(getTitle(config.getTranslationPrefix())); - this.parent = parent; - this.config = config; - this.widgets = EntryInfoWidgetBuilder.buildWidgets(config); - this.translationPrefix = config.getTranslationPrefix(); - } public static Text getTitle(String categoryPath) { final String titlePath = categoryPath + "title"; if (JfConfigSafe.TRANSLATION_SUPPLIER.apply(titlePath) != null) { @@ -41,22 +35,69 @@ public class TinyConfigScreen extends Screen { } return Text.translatable(titlePath); } - private final String translationPrefix; private final Screen parent; private final ConfigCategory config; - private final List> widgets; - private EntryListWidget list; + public final List> widgets; + private final Placeholder placeholder; + private final TabManager tabManager = new TabManager(a -> selectTab(((TinyConfigTabWrapper)a).getTab()), a -> {}); + private List tabs; + public ButtonWidget done; + private boolean reload = false; + + public TinyConfigScreen(ConfigCategory config, Screen parent) { + super(getTitle(config.getTranslationPrefix())); + this.parent = parent; + this.config = config; + this.widgets = new LinkedList<>(); + this.placeholder = new Placeholder<>(null); + } + + @Override + public void tick() { + super.tick(); + tabManager.tick(); + } @Override protected void init() { super.init(); - config.fix(); - for (WidgetState widget : widgets) { - widget.updateCache(); + this.done = ButtonWidget.builder(ScreenTexts.DONE, button -> { + for (WidgetState state : widgets) { + Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e)); + } + config.getRoot().write(); + Objects.requireNonNull(client).setScreen(parent); + }) + .position(this.width / 2 + 4, this.height - 28) + .size(150, 20) + .build(); + + if (!reload) { + if (config.getEntries().isEmpty() + && config.getPresets().keySet().stream().allMatch(s -> s.equals(CategoryBuilder.CONFIG_PRESET_DEFAULT)) + && config.getReferencedConfigs().isEmpty() + ) { + this.tabs = config.getCategories() + .values() + .stream() + .map(c -> new TinyConfigTab(this, c, textRenderer, false)) + .toList(); + } else { + this.tabs = List.of(); + } + if (this.tabs.isEmpty()) this.tabs = List.of(new TinyConfigTab(this, config, textRenderer, true)); } - if (!config.getPresets().isEmpty()) { + TabNavigationWidget tabNavigation = TabNavigationWidget.builder(tabManager, this.width) + .tabs(tabs.toArray(TinyConfigTab[]::new)) + .build(); + + if (tabs.size() > 1) this.addDrawableChild(tabNavigation); + tabNavigation.selectTab(0, false); + tabNavigation.init(); + + if (tabs.size() == 1 && !config.getPresets().isEmpty()) { this.addDrawableChild(ButtonWidget.builder(Text.translatable("libjf-config-v1.presets"), button -> Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config))) .position(4, 6) @@ -70,73 +111,41 @@ public class TinyConfigScreen extends Screen { .size(150, 20) .build()); - ButtonWidget done = this.addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, (button) -> { - for (WidgetState state : widgets) { - Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e)); - } - config.getRoot().write(); - Objects.requireNonNull(client).setScreen(parent); - }) - .position(this.width / 2 + 4, this.height - 28) - .size(150, 20) - .build()); - this.list = new EntryListWidget(this.client, this.width, this.height, 32, this.height - 32, 25); - this.addSelectableChild(this.list); - for (Map.Entry entry : config.getCategories().entrySet()) { - this.list.addReference(width / 2, - getTitle(entry.getValue().getTranslationPrefix()), - () -> new TinyConfigScreen(entry.getValue(), this)); - } - for (WidgetState info : widgets) { - MutableText name = Text.translatable(translationPrefix + info.entry.getName()); - WidgetFactory.Widget control = info.factory.build(width, textRenderer, done); - ButtonWidget resetButton = ButtonWidget.builder(Text.translatable("libjf-config-v1.reset"), (button -> { - info.reset(); - control.updateControls().run(); - })) - .position(width - 155, 0) - .size(40, 20) - .build(); - this.list.addButton(control.control(), resetButton, () -> { - boolean visible = !Objects.equals(info.entry.getDefault(), info.cachedValue); - resetButton.active = visible; - return visible; - }, name); - } - for (ConfigInstance ci : config.getReferencedConfigs()) { - if (ci != null) { - this.list.addReference(width / 2, - Text.translatable("libjf-config-v1.see-also", getTitle(ci.getTranslationPrefix())), - () -> new TinyConfigScreen(ci, this)); - } - } + this.addSelectableChild(this.placeholder); + this.addDrawableChild(done); + + reload = true; + } + + private void selectTab(TinyConfigTab tab) { + placeholder.setChild(tab.getList()); } @Override public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { this.renderBackground(matrices); - this.list.render(matrices, mouseX, mouseY, delta); + this.placeholder.render(matrices, mouseX, mouseY, delta); drawCenteredTextWithShadow(matrices, textRenderer, title, width / 2, 15, 0xFFFFFF); - Optional hovered = list.getHoveredEntryTitle(mouseY); + Optional hovered = placeholder.getChild().getHoveredEntryTitle(mouseY); if (hovered.isPresent()) { for (WidgetState info : widgets) { Text text = hovered.get(); - MutableText name = Text.translatable(this.translationPrefix + info.entry.getName()); + MutableText name = Text.translatable(info.id); boolean showTooltip = text.equals(name); - String tooltipKey = translationPrefix + info.entry.getName() + ".tooltip"; + String tooltipKey = info.id + ".tooltip"; if (showTooltip && info.error != null) { showTooltip = false; - renderTooltip(matrices, info.error.right(), mouseX, mouseY); + renderTooltip(matrices, info.error, mouseX, mouseY); } if (showTooltip && I18n.hasTranslation(tooltipKey)) { showTooltip = false; - List list = new ArrayList<>(); + List tooltip = new ArrayList<>(); for (String str : I18n.translate(tooltipKey).split("\n")) - list.add(Text.literal(str)); - renderTooltip(matrices, list, mouseX, mouseY); + tooltip.add(Text.literal(str)); + renderTooltip(matrices, tooltip, mouseX, mouseY); } } } @@ -147,4 +156,12 @@ public class TinyConfigScreen extends Screen { public void close() { Objects.requireNonNull(client).setScreen(parent); } + + public MinecraftClient getClient() { + return Objects.requireNonNull(client); + } + + public boolean hasClient() { + return client != null; + } } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTab.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTab.java new file mode 100644 index 0000000..bc0572d --- /dev/null +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTab.java @@ -0,0 +1,99 @@ +package io.gitlab.jfronny.libjf.config.impl.ui.tiny; + +import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.EntryInfoWidgetBuilder; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.EntryListWidget; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.presets.PresetsScreen; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.ScreenRect; +import net.minecraft.client.gui.tab.Tab; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; + +import java.util.*; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + +public class TinyConfigTab implements Tab { + private final ConfigCategory config; + private final EntryListWidget list; + + public TinyConfigTab(TinyConfigScreen screen, ConfigCategory config, TextRenderer textRenderer, boolean isRoot) { + this.config = config; + List> widgets = EntryInfoWidgetBuilder.buildWidgets(config, screen.widgets); + + config.fix(); + for (WidgetState widget : widgets) { + widget.updateCache(); + } + + this.list = new EntryListWidget(screen.getClient(), textRenderer, screen.width, screen.height, 32, screen.height - 32, 25); + if (screen.hasClient() && screen.getClient().world != null) this.list.setRenderBackground(false); + + if (isRoot) { + for (Map.Entry entry : config.getCategories().entrySet()) { + this.list.addReference(screen.width / 2, + TinyConfigScreen.getTitle(entry.getValue().getTranslationPrefix()), + () -> new TinyConfigScreen(entry.getValue(), screen)); + } + } else { + if (!config.getPresets().isEmpty()) { + this.list.addReference(screen.width / 2, + Text.translatable("libjf-config-v1.presets"), + () -> new PresetsScreen(screen, config)); + } + } + for (WidgetState info : widgets) { + MutableText name = Text.translatable(config.getTranslationPrefix() + info.entry.getName()); + WidgetFactory.Widget control = info.factory == null ? null : info.factory.build(screen, textRenderer); + ButtonWidget resetButton = ButtonWidget.builder(Text.translatable("libjf-config-v1.reset"), (button -> { + info.reset(); + if (control != null) control.updateControls().run(); + })) + .position(screen.width - 155, 0) + .size(40, 20) + .build(); + BooleanSupplier resetVisible = () -> { + boolean visible = !Objects.equals(info.entry.getDefault(), info.cachedValue); + resetButton.active = visible; + return visible; + }; + if (control == null) this.list.addUnknown(resetButton, resetVisible, name); + else this.list.addButton(control.control(), resetButton, resetVisible, name); + } + for (ConfigInstance ci : config.getReferencedConfigs()) { + if (ci != null) { + this.list.addReference(screen.width / 2, + Text.translatable("libjf-config-v1.see-also", TinyConfigScreen.getTitle(ci.getTranslationPrefix())), + () -> new TinyConfigScreen(ci, screen)); + } + } + } + + @Override + public Text getTitle() { + return TinyConfigScreen.getTitle(config.getTranslationPrefix()); + } + + @Override + public void forEachChild(Consumer consumer) { + consumer.accept(new TinyConfigTabWrapper(this)); + } + + @Override + public void refreshGrid(ScreenRect tabArea) { + } + + @Override + public void tick() { + Tab.super.tick(); + } + + public EntryListWidget getList() { + return list; + } +} diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTabWrapper.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTabWrapper.java new file mode 100644 index 0000000..cb4df55 --- /dev/null +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigTabWrapper.java @@ -0,0 +1,36 @@ +package io.gitlab.jfronny.libjf.config.impl.ui.tiny; + +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; + +public class TinyConfigTabWrapper extends ClickableWidget { + private final TinyConfigTab tab; + + public TinyConfigTabWrapper(TinyConfigTab tab) { + super(0, 0, 1, 1, Text.literal("")); + this.tab = tab; + visible = false; + active = false; + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + renderButton(matrices, mouseX, mouseY, delta); + } + + @Override + public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) { + throw new RuntimeException("TinyConfigTabWrapper must not be rendered"); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + throw new RuntimeException("TinyConfigTabWrapper must not be rendered"); + } + + public TinyConfigTab getTab() { + return tab; + } +} diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java index 97e883c..3cba779 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java @@ -1,28 +1,29 @@ package io.gitlab.jfronny.libjf.config.impl.ui.tiny; -import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo; import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory; -import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; import java.util.List; public class WidgetState { public EntryInfo entry; public List> knownStates; + public String id; - public Tuple error; + public Text error; public boolean inLimits = true; public String tempValue; public T cachedValue; - public WidgetFactory factory; + @Nullable public WidgetFactory factory; - public void initialize(EntryInfo entry, List> knownStates, WidgetFactory factory) { + public void initialize(EntryInfo entry, List> knownStates, @Nullable WidgetFactory factory, String prefix) { this.entry = entry; this.knownStates = knownStates; this.factory = factory; + this.id = prefix + entry.getName(); updateCache(); } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java index f7d207d..87f4c59 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java @@ -1,8 +1,6 @@ package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry; -import io.gitlab.jfronny.commons.ref.R; import io.gitlab.jfronny.commons.throwable.Try; -import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo; @@ -16,7 +14,8 @@ import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.text.Text; import net.minecraft.util.Formatting; -import java.util.*; +import java.util.LinkedList; +import java.util.List; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.regex.Pattern; @@ -26,12 +25,14 @@ public class EntryInfoWidgetBuilder { private static final Pattern INTEGER_ONLY = Pattern.compile("(-?\\d*)"); private static final Pattern DECIMAL_ONLY = Pattern.compile("-?(\\d+\\.?\\d*|\\d*\\.?\\d+|\\.)"); - public static List> buildWidgets(ConfigCategory config) { - List> knownStates = new ArrayList<>(); + public static List> buildWidgets(ConfigCategory config, List> knownStates) { + List> knownStates2 = new LinkedList<>(); for (EntryInfo info : config.getEntries()) { - knownStates.add(initEntry(config, info, knownStates)); + WidgetState state = initEntry(config, info, knownStates); + knownStates.add(state); + knownStates2.add(state); } - return knownStates; + return knownStates2; } private static WidgetState initEntry(ConfigCategory config, EntryInfo info, List> knownStates) { @@ -75,13 +76,10 @@ public class EntryInfoWidgetBuilder { }); } else { LibJf.LOGGER.error("Unsupported entry type in " + info.getName() + ": " + type.getName() + " - not displaying config control"); - factory = ((screenWidth, textRenderer, done) -> new WidgetFactory.Widget(R::nop, ButtonWidget.builder(Text.of(""), R::nop) - .position(-10, 0) - .size(0, 0) - .build())); + factory = null; } - Try.orThrow(() -> state.initialize(info, knownStates, factory)); + Try.orThrow(() -> state.initialize(info, knownStates, factory, config.getTranslationPrefix())); return state; } @@ -93,11 +91,11 @@ public class EntryInfoWidgetBuilder { } private static WidgetFactory toggle(EntryInfo info, WidgetState state, UnaryOperator increment, Function valueToText) { - return (screenWidth, textRenderer, done) -> { + return (screen, textRenderer) -> { final ButtonWidget button = ButtonWidget.builder(valueToText.apply(state.cachedValue), btn -> { - state.updateCache((T) increment.apply(state.cachedValue)); + state.updateCache(increment.apply(state.cachedValue)); btn.setMessage(valueToText.apply(state.cachedValue)); - }).position(screenWidth - 110, 0).size(info.getWidth(), 20).build(); + }).position(screen.width - 110, 0).size(info.getWidth(), 20).build(); return new WidgetFactory.Widget(() -> button.setMessage(valueToText.apply(state.cachedValue)), button); }; } @@ -113,8 +111,8 @@ public class EntryInfoWidgetBuilder { */ private static WidgetFactory textField(EntryInfo info, WidgetState state, Pattern pattern, Function sizeFetcher, boolean wholeNumber, double min, double max) { boolean isNumber = pattern != null; - return (width, textRenderer, done) -> { - TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.getWidth(), 20, null); + return (screen, textRenderer) -> { + TextFieldWidget widget = new TextFieldWidget(textRenderer, screen.width - 110, 0, info.getWidth(), 20, null); widget.setText(state.tempValue); widget.setTextPredicate(currentInput -> { @@ -127,15 +125,16 @@ public class EntryInfoWidgetBuilder { if (!(isNumber && currentInput.isEmpty()) && !currentInput.equals("-") && !currentInput.equals(".")) { value = sizeFetcher.apply(currentInput); inLimits = value.doubleValue() >= min && value.doubleValue() <= max; - state.error = inLimits ? null : Tuple.of(widget, Text.literal(value.doubleValue() < min ? + state.error = inLimits ? null : Text.literal(value.doubleValue() < min ? "§cMinimum " + (isNumber? "value" : "length") + (wholeNumber ? " is " + (int) min : " is " + min) : - "§cMaximum " + (isNumber? "value" : "length") + (wholeNumber ? " is " + (int) max : " is " + max))); + "§cMaximum " + (isNumber? "value" : "length") + (wholeNumber ? " is " + (int) max : " is " + max)) + .formatted(Formatting.RED); } state.tempValue = currentInput; widget.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777); state.inLimits = inLimits; - done.active = state.knownStates.stream().allMatch(st -> st.inLimits); + screen.done.active = state.knownStates.stream().allMatch(st -> st.inLimits); if (inLimits) { state.cachedValue = isNumber ? (T) value : (T) currentInput; @@ -153,8 +152,8 @@ public class EntryInfoWidgetBuilder { double max = info.getMaxValue(); if (!isDiscrete(min)) throw new IllegalArgumentException("Attempted to create slider with indiscrete minimum"); if (!isDiscrete(max)) throw new IllegalArgumentException("Attempted to create slider with indiscrete maximum"); - return (width, textRenderer, done) -> { - CustomSlider slider = new CustomSlider(width - 110, 0, info.getWidth(), 20, Double.parseDouble(state.tempValue), min, max, v -> { + return (screen, textRenderer) -> { + CustomSlider slider = new CustomSlider(screen.width - 110, 0, info.getWidth(), 20, Double.parseDouble(state.tempValue), min, max, v -> { state.updateCache(d2t.apply(v)); }, wholeNumber); diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java index d1b2f37..f41e783 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java @@ -12,20 +12,20 @@ import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.gui.widget.ElementListWidget; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; -import java.util.function.BooleanSupplier; -import java.util.function.Supplier; +import java.util.function.*; @Environment(EnvType.CLIENT) public class EntryListWidget extends ElementListWidget { TextRenderer textRenderer; - public EntryListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l, int m) { + public EntryListWidget(MinecraftClient minecraftClient, TextRenderer tr, int i, int j, int k, int l, int m) { super(minecraftClient, i, j, k, l, m); this.centerListVertically = false; - textRenderer = minecraftClient.textRenderer; + textRenderer = tr; } @Override @@ -33,7 +33,11 @@ public class EntryListWidget extends ElementListWidget= entry.button.getY() && mouseY < entry.button.getY() + itemHeight) { return Optional.of(entry.getText()); + } else if (abstractEntry instanceof ConfigUnknownEntry entry + && mouseY >= entry.resetButton.getY() && mouseY < entry.resetButton.getY() + itemHeight) { + return Optional.of(entry.getText()); } } return Optional.empty(); } @Environment(EnvType.CLIENT) - public static abstract class ConfigEntry extends Entry { + public abstract static class ConfigEntry extends Entry { } @Environment(EnvType.CLIENT) @@ -129,4 +136,41 @@ public class EntryListWidget extends ElementListWidget children() { + return List.of(resetButton); + } + + @Override + public List selectableChildren() { + return List.of(); + } + + public Text getText() { + return text; + } + } } diff --git a/src/main/resources/assets/libjf/icon.png b/src/client/resources/assets/libjf/icon.png similarity index 100% rename from src/main/resources/assets/libjf/icon.png rename to src/client/resources/assets/libjf/icon.png