[config-ui-tiny] use tabs if upper level contains nothing of note
This commit is contained in:
parent
0bc1ec1b5d
commit
e640fe5145
@ -1,6 +1,6 @@
|
|||||||
# https://fabricmc.net/develop/
|
# https://fabricmc.net/develop/
|
||||||
minecraft_version=1.19.4-pre1
|
minecraft_version=1.19.4-rc2
|
||||||
yarn_mappings=build.6
|
yarn_mappings=build.1
|
||||||
loader_version=0.14.17
|
loader_version=0.14.17
|
||||||
|
|
||||||
maven_group=io.gitlab.jfronny.libjf
|
maven_group=io.gitlab.jfronny.libjf
|
||||||
@ -14,7 +14,7 @@ modrinth_optional_dependencies=fabric-api
|
|||||||
curseforge_id=482600
|
curseforge_id=482600
|
||||||
curseforge_optional_dependencies=fabric-api
|
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
|
commons_version=1.1-SNAPSHOT
|
||||||
gson_compile_version=1.2-SNAPSHOT
|
gson_compile_version=1.2-SNAPSHOT
|
||||||
modmenu_version=6.1.0-beta.3
|
modmenu_version=6.1.0-beta.3
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package io.gitlab.jfronny.libjf.config.api.v1.ui.tiny;
|
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.font.TextRenderer;
|
||||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
|
||||||
import net.minecraft.client.gui.widget.ClickableWidget;
|
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||||
|
|
||||||
public interface WidgetFactory {
|
public interface WidgetFactory {
|
||||||
Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done);
|
Widget build(TinyConfigScreen screen, TextRenderer textRenderer);
|
||||||
|
|
||||||
record Widget(Runnable updateControls, ClickableWidget control) {
|
record Widget(Runnable updateControls, ClickableWidget control) {
|
||||||
}
|
}
|
||||||
|
@ -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<T extends Element & Selectable & Drawable> 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();
|
||||||
|
}
|
||||||
|
}
|
@ -3,16 +3,17 @@ package io.gitlab.jfronny.libjf.config.impl.ui.tiny;
|
|||||||
import io.gitlab.jfronny.commons.throwable.Try;
|
import io.gitlab.jfronny.commons.throwable.Try;
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory;
|
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.dsl.CategoryBuilder;
|
||||||
import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory;
|
|
||||||
import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe;
|
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.entry.EntryListWidget;
|
||||||
import io.gitlab.jfronny.libjf.config.impl.ui.tiny.presets.PresetsScreen;
|
import io.gitlab.jfronny.libjf.config.impl.ui.tiny.presets.PresetsScreen;
|
||||||
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.gui.screen.Screen;
|
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.ButtonWidget;
|
||||||
|
import net.minecraft.client.gui.widget.TabNavigationWidget;
|
||||||
import net.minecraft.client.resource.language.I18n;
|
import net.minecraft.client.resource.language.I18n;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.screen.ScreenTexts;
|
import net.minecraft.screen.ScreenTexts;
|
||||||
@ -23,13 +24,6 @@ import java.util.*;
|
|||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
public class TinyConfigScreen extends Screen {
|
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) {
|
public static Text getTitle(String categoryPath) {
|
||||||
final String titlePath = categoryPath + "title";
|
final String titlePath = categoryPath + "title";
|
||||||
if (JfConfigSafe.TRANSLATION_SUPPLIER.apply(titlePath) != null) {
|
if (JfConfigSafe.TRANSLATION_SUPPLIER.apply(titlePath) != null) {
|
||||||
@ -41,22 +35,69 @@ public class TinyConfigScreen extends Screen {
|
|||||||
}
|
}
|
||||||
return Text.translatable(titlePath);
|
return Text.translatable(titlePath);
|
||||||
}
|
}
|
||||||
private final String translationPrefix;
|
|
||||||
private final Screen parent;
|
private final Screen parent;
|
||||||
private final ConfigCategory config;
|
private final ConfigCategory config;
|
||||||
private final List<WidgetState<?>> widgets;
|
public final List<WidgetState<?>> widgets;
|
||||||
private EntryListWidget list;
|
private final Placeholder<EntryListWidget> placeholder;
|
||||||
|
private final TabManager tabManager = new TabManager(a -> selectTab(((TinyConfigTabWrapper)a).getTab()), a -> {});
|
||||||
|
private List<TinyConfigTab> 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
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
config.fix();
|
this.done = ButtonWidget.builder(ScreenTexts.DONE, button -> {
|
||||||
for (WidgetState<?> widget : widgets) {
|
for (WidgetState<?> state : widgets) {
|
||||||
widget.updateCache();
|
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"),
|
this.addDrawableChild(ButtonWidget.builder(Text.translatable("libjf-config-v1.presets"),
|
||||||
button -> Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config)))
|
button -> Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config)))
|
||||||
.position(4, 6)
|
.position(4, 6)
|
||||||
@ -70,73 +111,41 @@ public class TinyConfigScreen extends Screen {
|
|||||||
.size(150, 20)
|
.size(150, 20)
|
||||||
.build());
|
.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.placeholder);
|
||||||
this.addSelectableChild(this.list);
|
this.addDrawableChild(done);
|
||||||
for (Map.Entry<String, ConfigCategory> entry : config.getCategories().entrySet()) {
|
|
||||||
this.list.addReference(width / 2,
|
reload = true;
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void selectTab(TinyConfigTab tab) {
|
||||||
|
placeholder.setChild(tab.getList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||||
this.renderBackground(matrices);
|
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);
|
drawCenteredTextWithShadow(matrices, textRenderer, title, width / 2, 15, 0xFFFFFF);
|
||||||
|
|
||||||
Optional<Text> hovered = list.getHoveredEntryTitle(mouseY);
|
Optional<Text> hovered = placeholder.getChild().getHoveredEntryTitle(mouseY);
|
||||||
if (hovered.isPresent()) {
|
if (hovered.isPresent()) {
|
||||||
for (WidgetState<?> info : widgets) {
|
for (WidgetState<?> info : widgets) {
|
||||||
Text text = hovered.get();
|
Text text = hovered.get();
|
||||||
MutableText name = Text.translatable(this.translationPrefix + info.entry.getName());
|
MutableText name = Text.translatable(info.id);
|
||||||
boolean showTooltip = text.equals(name);
|
boolean showTooltip = text.equals(name);
|
||||||
String tooltipKey = translationPrefix + info.entry.getName() + ".tooltip";
|
String tooltipKey = info.id + ".tooltip";
|
||||||
if (showTooltip && info.error != null) {
|
if (showTooltip && info.error != null) {
|
||||||
showTooltip = false;
|
showTooltip = false;
|
||||||
renderTooltip(matrices, info.error.right(), mouseX, mouseY);
|
renderTooltip(matrices, info.error, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
if (showTooltip && I18n.hasTranslation(tooltipKey)) {
|
if (showTooltip && I18n.hasTranslation(tooltipKey)) {
|
||||||
showTooltip = false;
|
showTooltip = false;
|
||||||
List<Text> list = new ArrayList<>();
|
List<Text> tooltip = new ArrayList<>();
|
||||||
for (String str : I18n.translate(tooltipKey).split("\n"))
|
for (String str : I18n.translate(tooltipKey).split("\n"))
|
||||||
list.add(Text.literal(str));
|
tooltip.add(Text.literal(str));
|
||||||
renderTooltip(matrices, list, mouseX, mouseY);
|
renderTooltip(matrices, tooltip, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,4 +156,12 @@ public class TinyConfigScreen extends Screen {
|
|||||||
public void close() {
|
public void close() {
|
||||||
Objects.requireNonNull(client).setScreen(parent);
|
Objects.requireNonNull(client).setScreen(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MinecraftClient getClient() {
|
||||||
|
return Objects.requireNonNull(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasClient() {
|
||||||
|
return client != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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<WidgetState<?>> 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<String, ConfigCategory> 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<ClickableWidget> consumer) {
|
||||||
|
consumer.accept(new TinyConfigTabWrapper(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshGrid(ScreenRect tabArea) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
Tab.super.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntryListWidget getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,29 @@
|
|||||||
package io.gitlab.jfronny.libjf.config.impl.ui.tiny;
|
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.LibJf;
|
||||||
import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo;
|
import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo;
|
||||||
import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory;
|
import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory;
|
||||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WidgetState<T> {
|
public class WidgetState<T> {
|
||||||
public EntryInfo<T> entry;
|
public EntryInfo<T> entry;
|
||||||
public List<WidgetState<?>> knownStates;
|
public List<WidgetState<?>> knownStates;
|
||||||
|
public String id;
|
||||||
|
|
||||||
public Tuple<TextFieldWidget, Text> error;
|
public Text error;
|
||||||
public boolean inLimits = true;
|
public boolean inLimits = true;
|
||||||
public String tempValue;
|
public String tempValue;
|
||||||
public T cachedValue;
|
public T cachedValue;
|
||||||
public WidgetFactory factory;
|
@Nullable public WidgetFactory factory;
|
||||||
|
|
||||||
public void initialize(EntryInfo<T> entry, List<WidgetState<?>> knownStates, WidgetFactory factory) {
|
public void initialize(EntryInfo<T> entry, List<WidgetState<?>> knownStates, @Nullable WidgetFactory factory, String prefix) {
|
||||||
this.entry = entry;
|
this.entry = entry;
|
||||||
this.knownStates = knownStates;
|
this.knownStates = knownStates;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
|
this.id = prefix + entry.getName();
|
||||||
updateCache();
|
updateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry;
|
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.throwable.Try;
|
||||||
import io.gitlab.jfronny.commons.tuple.Tuple;
|
|
||||||
import io.gitlab.jfronny.libjf.LibJf;
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory;
|
import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory;
|
||||||
import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo;
|
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.text.Text;
|
||||||
import net.minecraft.util.Formatting;
|
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.Function;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.regex.Pattern;
|
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 INTEGER_ONLY = Pattern.compile("(-?\\d*)");
|
||||||
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?(\\d+\\.?\\d*|\\d*\\.?\\d+|\\.)");
|
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?(\\d+\\.?\\d*|\\d*\\.?\\d+|\\.)");
|
||||||
|
|
||||||
public static List<WidgetState<?>> buildWidgets(ConfigCategory config) {
|
public static List<WidgetState<?>> buildWidgets(ConfigCategory config, List<WidgetState<?>> knownStates) {
|
||||||
List<WidgetState<?>> knownStates = new ArrayList<>();
|
List<WidgetState<?>> knownStates2 = new LinkedList<>();
|
||||||
for (EntryInfo<?> info : config.getEntries()) {
|
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 <T> WidgetState<T> initEntry(ConfigCategory config, EntryInfo<T> info, List<WidgetState<?>> knownStates) {
|
private static <T> WidgetState<T> initEntry(ConfigCategory config, EntryInfo<T> info, List<WidgetState<?>> knownStates) {
|
||||||
@ -75,13 +76,10 @@ public class EntryInfoWidgetBuilder {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
LibJf.LOGGER.error("Unsupported entry type in " + info.getName() + ": " + type.getName() + " - not displaying config control");
|
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)
|
factory = null;
|
||||||
.position(-10, 0)
|
|
||||||
.size(0, 0)
|
|
||||||
.build()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Try.orThrow(() -> state.initialize(info, knownStates, factory));
|
Try.orThrow(() -> state.initialize(info, knownStates, factory, config.getTranslationPrefix()));
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +91,11 @@ public class EntryInfoWidgetBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static <T> WidgetFactory toggle(EntryInfo<T> info, WidgetState<T> state, UnaryOperator<T> increment, Function<T, Text> valueToText) {
|
private static <T> WidgetFactory toggle(EntryInfo<T> info, WidgetState<T> state, UnaryOperator<T> increment, Function<T, Text> valueToText) {
|
||||||
return (screenWidth, textRenderer, done) -> {
|
return (screen, textRenderer) -> {
|
||||||
final ButtonWidget button = ButtonWidget.builder(valueToText.apply(state.cachedValue), btn -> {
|
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));
|
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);
|
return new WidgetFactory.Widget(() -> button.setMessage(valueToText.apply(state.cachedValue)), button);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -113,8 +111,8 @@ public class EntryInfoWidgetBuilder {
|
|||||||
*/
|
*/
|
||||||
private static <T> WidgetFactory textField(EntryInfo<T> info, WidgetState<T> state, Pattern pattern, Function<String, Number> sizeFetcher, boolean wholeNumber, double min, double max) {
|
private static <T> WidgetFactory textField(EntryInfo<T> info, WidgetState<T> state, Pattern pattern, Function<String, Number> sizeFetcher, boolean wholeNumber, double min, double max) {
|
||||||
boolean isNumber = pattern != null;
|
boolean isNumber = pattern != null;
|
||||||
return (width, textRenderer, done) -> {
|
return (screen, textRenderer) -> {
|
||||||
TextFieldWidget widget = new TextFieldWidget(textRenderer, width - 110, 0, info.getWidth(), 20, null);
|
TextFieldWidget widget = new TextFieldWidget(textRenderer, screen.width - 110, 0, info.getWidth(), 20, null);
|
||||||
|
|
||||||
widget.setText(state.tempValue);
|
widget.setText(state.tempValue);
|
||||||
widget.setTextPredicate(currentInput -> {
|
widget.setTextPredicate(currentInput -> {
|
||||||
@ -127,15 +125,16 @@ public class EntryInfoWidgetBuilder {
|
|||||||
if (!(isNumber && currentInput.isEmpty()) && !currentInput.equals("-") && !currentInput.equals(".")) {
|
if (!(isNumber && currentInput.isEmpty()) && !currentInput.equals("-") && !currentInput.equals(".")) {
|
||||||
value = sizeFetcher.apply(currentInput);
|
value = sizeFetcher.apply(currentInput);
|
||||||
inLimits = value.doubleValue() >= min && value.doubleValue() <= max;
|
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) :
|
"§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;
|
state.tempValue = currentInput;
|
||||||
widget.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
|
widget.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
|
||||||
state.inLimits = inLimits;
|
state.inLimits = inLimits;
|
||||||
done.active = state.knownStates.stream().allMatch(st -> st.inLimits);
|
screen.done.active = state.knownStates.stream().allMatch(st -> st.inLimits);
|
||||||
|
|
||||||
if (inLimits) {
|
if (inLimits) {
|
||||||
state.cachedValue = isNumber ? (T) value : (T) currentInput;
|
state.cachedValue = isNumber ? (T) value : (T) currentInput;
|
||||||
@ -153,8 +152,8 @@ public class EntryInfoWidgetBuilder {
|
|||||||
double max = info.getMaxValue();
|
double max = info.getMaxValue();
|
||||||
if (!isDiscrete(min)) throw new IllegalArgumentException("Attempted to create slider with indiscrete minimum");
|
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");
|
if (!isDiscrete(max)) throw new IllegalArgumentException("Attempted to create slider with indiscrete maximum");
|
||||||
return (width, textRenderer, done) -> {
|
return (screen, textRenderer) -> {
|
||||||
CustomSlider slider = new CustomSlider(width - 110, 0, info.getWidth(), 20, Double.parseDouble(state.tempValue), min, max, v -> {
|
CustomSlider slider = new CustomSlider(screen.width - 110, 0, info.getWidth(), 20, Double.parseDouble(state.tempValue), min, max, v -> {
|
||||||
state.updateCache(d2t.apply(v));
|
state.updateCache(d2t.apply(v));
|
||||||
}, wholeNumber);
|
}, wholeNumber);
|
||||||
|
|
||||||
|
@ -12,20 +12,20 @@ import net.minecraft.client.gui.widget.ClickableWidget;
|
|||||||
import net.minecraft.client.gui.widget.ElementListWidget;
|
import net.minecraft.client.gui.widget.ElementListWidget;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.*;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEntry> {
|
public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEntry> {
|
||||||
TextRenderer textRenderer;
|
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);
|
super(minecraftClient, i, j, k, l, m);
|
||||||
this.centerListVertically = false;
|
this.centerListVertically = false;
|
||||||
textRenderer = minecraftClient.textRenderer;
|
textRenderer = tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -33,7 +33,11 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||||||
return this.width -7;
|
return this.width -7;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addButton(ClickableWidget button, ClickableWidget resetButton, BooleanSupplier resetVisible, Text text) {
|
public void addUnknown(ClickableWidget resetButton, BooleanSupplier resetVisible, Text text) {
|
||||||
|
this.addEntry(new ConfigUnknownEntry(text, resetVisible, resetButton));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addButton(@Nullable ClickableWidget button, ClickableWidget resetButton, BooleanSupplier resetVisible, Text text) {
|
||||||
this.addEntry(new ConfigScreenEntry(button, text, resetVisible, resetButton));
|
this.addEntry(new ConfigScreenEntry(button, text, resetVisible, resetButton));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,13 +56,16 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||||||
&& entry.button.visible
|
&& entry.button.visible
|
||||||
&& mouseY >= entry.button.getY() && mouseY < entry.button.getY() + itemHeight) {
|
&& mouseY >= entry.button.getY() && mouseY < entry.button.getY() + itemHeight) {
|
||||||
return Optional.of(entry.getText());
|
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();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
public static abstract class ConfigEntry extends Entry<ConfigEntry> {
|
public abstract static class ConfigEntry extends Entry<ConfigEntry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
@ -129,4 +136,41 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Environment(EnvType.CLIENT)
|
||||||
|
public static class ConfigUnknownEntry extends ConfigEntry {
|
||||||
|
private static final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
|
||||||
|
private final BooleanSupplier resetVisible;
|
||||||
|
private final ClickableWidget resetButton;
|
||||||
|
private final Text text;
|
||||||
|
|
||||||
|
public ConfigUnknownEntry(Text text, BooleanSupplier resetVisible, ClickableWidget resetButton) {
|
||||||
|
this.resetVisible = resetVisible;
|
||||||
|
this.resetButton = resetButton;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||||
|
drawTextWithShadow(matrices,textRenderer, text,12,y+5,0xFFFFFF);
|
||||||
|
if (resetVisible.getAsBoolean()) {
|
||||||
|
resetButton.setY(y);
|
||||||
|
resetButton.render(matrices, mouseX, mouseY, tickDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends Element> children() {
|
||||||
|
return List.of(resetButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends Selectable> selectableChildren() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Text getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Loading…
Reference in New Issue
Block a user