aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisXander <isxander@users.noreply.github.com>2022-09-18 19:14:58 +0100
committerisXander <isxander@users.noreply.github.com>2022-09-18 19:15:09 +0100
commit33e98c7edc1404e099f9c9bcc586fd5c55cb8bdd (patch)
treea964082d62c3b13f5628c036fe49f9ea19d32315
parent564a028eed25cfce0e0486f1a1a21affb499a311 (diff)
downloadYetAnotherConfigLib-33e98c7edc1404e099f9c9bcc586fd5c55cb8bdd.tar.gz
YetAnotherConfigLib-33e98c7edc1404e099f9c9bcc586fd5c55cb8bdd.tar.bz2
YetAnotherConfigLib-33e98c7edc1404e099f9c9bcc586fd5c55cb8bdd.zip
1.3.0
option tooltips now consume the pending value PlaceholderCategory: a category that when selected, just opens a screen
-rw-r--r--build.gradle.kts2
-rw-r--r--changelogs/1.3.0.md2
-rw-r--r--src/main/java/dev/isxander/yacl/api/ConfigCategory.java2
-rw-r--r--src/main/java/dev/isxander/yacl/api/Option.java42
-rw-r--r--src/main/java/dev/isxander/yacl/api/PlaceholderCategory.java94
-rw-r--r--src/main/java/dev/isxander/yacl/gui/YACLScreen.java36
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java11
-rw-r--r--src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java11
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionImpl.java23
-rw-r--r--src/main/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java19
-rw-r--r--src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java8
11 files changed, 198 insertions, 52 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index 7e46d96..74f830a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,7 +16,7 @@ plugins {
val ciRun = System.getenv().containsKey("GITHUB_ACTIONS")
group = "dev.isxander"
-version = "1.2.1"
+version = "1.3.0"
if (ciRun)
version = "$version-SNAPSHOT"
diff --git a/changelogs/1.3.0.md b/changelogs/1.3.0.md
new file mode 100644
index 0000000..f088eb5
--- /dev/null
+++ b/changelogs/1.3.0.md
@@ -0,0 +1,2 @@
+- Option tooltips now have access to the pending value to dynamically change
+- `PlaceholderCategory`: A category that when selected, just opens a screen
diff --git a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
index 1b2a2bc..27c3e95 100644
--- a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
+++ b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
@@ -90,7 +90,7 @@ public interface ConfigCategory {
* @see OptionGroup#isRoot()
*/
public Builder options(@NotNull Collection<Option<?>> options) {
- Validate.notEmpty(options, "`options` must not be empty");
+ Validate.notNull(options, "`options` must not be null");
this.rootOptions.addAll(options);
return this;
diff --git a/src/main/java/dev/isxander/yacl/api/Option.java b/src/main/java/dev/isxander/yacl/api/Option.java
index ff72c4c..1c3b006 100644
--- a/src/main/java/dev/isxander/yacl/api/Option.java
+++ b/src/main/java/dev/isxander/yacl/api/Option.java
@@ -9,9 +9,11 @@ import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import java.util.*;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Stream;
public interface Option<T> {
/**
@@ -95,6 +97,8 @@ public interface Option<T> {
*/
void requestSetDefault();
+ void addListener(BiConsumer<Option<T>, T> changedListener);
+
/**
* Creates a builder to construct an {@link Option}
*
@@ -108,7 +112,7 @@ public interface Option<T> {
class Builder<T> {
private Text name = Text.literal("Name not specified!").formatted(Formatting.RED);
- private final List<Text> tooltipLines = new ArrayList<>();
+ private final List<Function<T, Text>> tooltipGetters = new ArrayList<>();
private Function<Option<T>, Controller<T>> controlGetter;
@@ -138,6 +142,20 @@ public interface Option<T> {
/**
* Sets the tooltip to be used by the option.
+ * No need to wrap the text yourself, the gui does this itself.
+ *
+ * @param tooltipGetter function to get tooltip depending on value {@link Builder#build()}.
+ */
+ @SafeVarargs
+ public final Builder<T> tooltip(@NotNull Function<T, Text>... tooltipGetter) {
+ Validate.notNull(tooltipGetter, "`tooltipGetter` cannot be null");
+
+ this.tooltipGetters.addAll(List.of(tooltipGetter));
+ return this;
+ }
+
+ /**
+ * Sets the tooltip to be used by the option.
* Can be invoked twice to append more lines.
* No need to wrap the text yourself, the gui does this itself.
*
@@ -146,7 +164,7 @@ public interface Option<T> {
public Builder<T> tooltip(@NotNull Text... tooltips) {
Validate.notNull(tooltips, "`tooltips` cannot be empty");
- tooltipLines.addAll(List.of(tooltips));
+ this.tooltipGetters.addAll(Stream.of(tooltips).map(text -> (Function<T, Text>) t -> text).toList());
return this;
}
@@ -244,16 +262,20 @@ public interface Option<T> {
Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
Validate.notNull(binding, "`binding` must not be null when building `Option`");
- MutableText concatenatedTooltip = Text.empty();
- boolean first = true;
- for (Text line : tooltipLines) {
- if (!first) concatenatedTooltip.append("\n");
- first = false;
+ Function<T, Text> concatenatedTooltipGetter = value -> {
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Function<T, Text> line : tooltipGetters) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line.apply(value));
+ }
- concatenatedTooltip.append(line);
- }
+ return concatenatedTooltip;
+ };
- return new OptionImpl<>(name, concatenatedTooltip, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass);
+ return new OptionImpl<>(name, concatenatedTooltipGetter, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass);
}
}
}
diff --git a/src/main/java/dev/isxander/yacl/api/PlaceholderCategory.java b/src/main/java/dev/isxander/yacl/api/PlaceholderCategory.java
new file mode 100644
index 0000000..de7441c
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/api/PlaceholderCategory.java
@@ -0,0 +1,94 @@
+package dev.isxander.yacl.api;
+
+import dev.isxander.yacl.gui.YACLScreen;
+import dev.isxander.yacl.impl.PlaceholderCategoryImpl;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+/**
+ * A placeholder category that actually just opens another screen,
+ * instead of displaying options
+ */
+public interface PlaceholderCategory extends ConfigCategory {
+ /**
+ * Function to create a screen to open upon changing to this category
+ */
+ BiFunction<MinecraftClient, YACLScreen, Screen> screen();
+
+ static Builder createBuilder() {
+ return new Builder();
+ }
+
+ class Builder {
+ private Text name;
+
+ private final List<Text> tooltipLines = new ArrayList<>();
+
+ private BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction;
+
+ private Builder() {
+
+ }
+
+ /**
+ * Sets name of the category
+ *
+ * @see ConfigCategory#name()
+ */
+ public Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` cannot be null");
+
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the tooltip to be used by the category.
+ * Can be invoked twice to append more lines.
+ * No need to wrap the text yourself, the gui does this itself.
+ *
+ * @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
+ */
+ public Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ /**
+ * Screen to open upon selecting this category
+ *
+ * @see PlaceholderCategory#screen()
+ */
+ public Builder screen(@NotNull BiFunction<MinecraftClient, YACLScreen, Screen> screenFunction) {
+ Validate.notNull(screenFunction, "`screenFunction` cannot be null");
+
+ this.screenFunction = screenFunction;
+ return this;
+ }
+
+ public PlaceholderCategory build() {
+ Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
+
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new PlaceholderCategoryImpl(name, screenFunction, concatenatedTooltip);
+ }
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
index 26f4ad5..80ffb0e 100644
--- a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
+++ b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
@@ -1,10 +1,7 @@
package dev.isxander.yacl.gui;
import com.mojang.blaze3d.systems.RenderSystem;
-import dev.isxander.yacl.api.ConfigCategory;
-import dev.isxander.yacl.api.Option;
-import dev.isxander.yacl.api.OptionFlag;
-import dev.isxander.yacl.api.YetAnotherConfigLib;
+import dev.isxander.yacl.api.*;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.api.utils.OptionUtils;
import dev.isxander.yacl.impl.YACLConstants;
@@ -32,13 +29,11 @@ public class YACLScreen extends Screen {
private final Screen parent;
public OptionListWidget optionList;
-// public final List<CategoryWidget> categoryButtons;
public CategoryListWidget categoryList;
public TooltipButtonWidget finishedSaveButton, cancelResetButton, undoButton;
public SearchFieldWidget searchFieldWidget;
- public Text saveButtonMessage;
- public Text saveButtonTooltipMessage;
+ public Text saveButtonMessage, saveButtonTooltipMessage;
private int saveButtonMessageTime;
@@ -46,34 +41,15 @@ public class YACLScreen extends Screen {
super(config.title());
this.config = config;
this.parent = parent;
-// this.categoryButtons = new ArrayList<>();
this.currentCategoryIdx = 0;
}
@Override
protected void init() {
-// categoryButtons.clear();
int columnWidth = width / 3;
int padding = columnWidth / 20;
columnWidth = Math.min(columnWidth, 400);
int paddedWidth = columnWidth - padding * 2;
-// Dimension<Integer> categoryDim = Dimension.ofInt(width / 3 / 2, padding, paddedWidth, 20);
-// int idx = 0;
-// for (ConfigCategory category : config.categories()) {
-// CategoryWidget categoryWidget = new CategoryWidget(
-// this,
-// category,
-// idx,
-// categoryDim.x() - categoryDim.width() / 2, categoryDim.y(),
-// categoryDim.width(), categoryDim.height()
-// );
-//
-// categoryButtons.add(categoryWidget);
-// addDrawableChild(categoryWidget);
-//
-// idx++;
-// categoryDim.move(0, 21);
-// }
Dimension<Integer> actionDim = Dimension.ofInt(width / 3 / 2, height - padding - 20, paddedWidth, 20);
finishedSaveButton = new TooltipButtonWidget(this, actionDim.x() - actionDim.width() / 2, actionDim.y(), actionDim.width(), actionDim.height(), Text.empty(), Text.empty(), (btn) -> {
@@ -167,8 +143,12 @@ public class YACLScreen extends Screen {
}
public void changeCategory(int idx) {
- currentCategoryIdx = idx;
- optionList.refreshOptions();
+ if (currentCategoryIdx != -1 && config.categories().get(idx) instanceof PlaceholderCategory placeholderCategory) {
+ client.setScreen(placeholderCategory.screen().apply(client, this));
+ } else {
+ currentCategoryIdx = idx;
+ optionList.refreshOptions();
+ }
}
private void updateActionAvailability() {
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java b/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
index 1c986e4..cd70872 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
@@ -16,7 +16,7 @@ import java.util.List;
public abstract class ControllerWidget<T extends Controller<?>> extends AbstractWidget {
protected final T control;
- protected final MultilineText wrappedTooltip;
+ protected MultilineText wrappedTooltip;
protected final YACLScreen screen;
protected boolean focused = false;
@@ -29,7 +29,8 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
super(dim);
this.control = control;
this.screen = screen;
- this.wrappedTooltip = MultilineText.create(textRenderer, control.option().tooltip(), screen.width / 2);
+ control.option().addListener((opt, pending) -> updateTooltip());
+ updateTooltip();
}
@Override
@@ -71,7 +72,7 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
@Override
public void postRender(MatrixStack matrices, int mouseX, int mouseY, float delta) {
- if (hoveredTicks > YACLConstants.HOVER_TICKS) {
+ if (hoveredTicks >= YACLConstants.HOVER_TICKS) {
YACLScreen.renderMultilineTooltip(matrices, textRenderer, wrappedTooltip, mouseX, mouseY, screen.width, screen.height);
}
}
@@ -94,6 +95,10 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
return this.dim.isPointInside((int) mouseX, (int) mouseY);
}
+ private void updateTooltip() {
+ this.wrappedTooltip = MultilineText.create(textRenderer, control.option().tooltip(), screen.width / 2);
+ }
+
protected int getControlWidth() {
return isHovered() ? getHoveredControlWidth() : getUnhoveredControlWidth();
}
diff --git a/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
index a600bc3..51c94b0 100644
--- a/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
+++ b/src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java
@@ -1,16 +1,14 @@
package dev.isxander.yacl.impl;
import com.google.common.collect.ImmutableSet;
-import dev.isxander.yacl.api.Binding;
-import dev.isxander.yacl.api.ButtonOption;
-import dev.isxander.yacl.api.Controller;
-import dev.isxander.yacl.api.OptionFlag;
+import dev.isxander.yacl.api.*;
import dev.isxander.yacl.gui.YACLScreen;
import net.minecraft.text.Text;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -113,6 +111,11 @@ public class ButtonOptionImpl implements ButtonOption {
}
+ @Override
+ public void addListener(BiConsumer<Option<Consumer<YACLScreen>>, Consumer<YACLScreen>> changedListener) {
+
+ }
+
private static class EmptyBinderImpl implements Binding<Consumer<YACLScreen>> {
@Override
public void setValue(Consumer<YACLScreen> value) {
diff --git a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
index 5a31a70..1cf2a79 100644
--- a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
+++ b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
@@ -10,12 +10,16 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
import java.util.function.Function;
@ApiStatus.Internal
public class OptionImpl<T> implements Option<T> {
private final Text name;
- private final Text tooltip;
+ private Text tooltip;
+ private final Function<T, Text> tooltipGetter;
private final Controller<T> controller;
private final Binding<T> binding;
private final boolean available;
@@ -26,9 +30,11 @@ public class OptionImpl<T> implements Option<T> {
private T pendingValue;
+ private final List<BiConsumer<Option<T>, T>> listeners;
+
public OptionImpl(
@NotNull Text name,
- @Nullable Text tooltip,
+ @Nullable Function<T, Text> tooltipGetter,
@NotNull Function<Option<T>, Controller<T>> controlGetter,
@NotNull Binding<T> binding,
boolean available,
@@ -36,13 +42,16 @@ public class OptionImpl<T> implements Option<T> {
@NotNull Class<T> typeClass
) {
this.name = name;
- this.tooltip = tooltip;
+ this.tooltipGetter = tooltipGetter;
this.controller = controlGetter.apply(this);
this.binding = binding;
this.available = available;
this.flags = flags;
this.typeClass = typeClass;
- this.pendingValue = binding().getValue();
+ this.listeners = new ArrayList<>();
+
+ addListener((opt, pending) -> tooltip = tooltipGetter.apply(pending));
+ requestSet(binding().getValue());
}
@Override
@@ -98,6 +107,7 @@ public class OptionImpl<T> implements Option<T> {
@Override
public void requestSet(T value) {
pendingValue = value;
+ listeners.forEach(listener -> listener.accept(this, pendingValue));
}
@Override
@@ -118,4 +128,9 @@ public class OptionImpl<T> implements Option<T> {
public void requestSetDefault() {
pendingValue = binding().defaultValue();
}
+
+ @Override
+ public void addListener(BiConsumer<Option<T>, T> changedListener) {
+ this.listeners.add(changedListener);
+ }
}
diff --git a/src/main/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java b/src/main/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java
new file mode 100644
index 0000000..a5180ad
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java
@@ -0,0 +1,19 @@
+package dev.isxander.yacl.impl;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.api.OptionGroup;
+import dev.isxander.yacl.api.PlaceholderCategory;
+import dev.isxander.yacl.gui.YACLScreen;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.BiFunction;
+
+public record PlaceholderCategoryImpl(Text name, BiFunction<MinecraftClient, YACLScreen, Screen> screen, Text tooltip) implements PlaceholderCategory {
+ @Override
+ public @NotNull ImmutableList<OptionGroup> groups() {
+ return ImmutableList.of();
+ }
+}
diff --git a/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java b/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java
index df81768..944246d 100644
--- a/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java
+++ b/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java
@@ -3,6 +3,7 @@ package dev.isxander.yacl.test;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import dev.isxander.yacl.api.*;
+import dev.isxander.yacl.gui.RequireRestartScreen;
import dev.isxander.yacl.gui.controllers.*;
import dev.isxander.yacl.gui.controllers.slider.DoubleSliderController;
import dev.isxander.yacl.gui.controllers.slider.FloatSliderController;
@@ -10,6 +11,7 @@ import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController;
import dev.isxander.yacl.gui.controllers.slider.LongSliderController;
import dev.isxander.yacl.gui.controllers.string.StringController;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.MessageScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.option.GraphicsMode;
import net.minecraft.client.toast.SystemToast;
@@ -60,7 +62,7 @@ public class ModMenuIntegration implements ModMenuApi {
.collapsed(true)
.option(Option.createBuilder(boolean.class)
.name(Text.of("Boolean Toggle"))
- .tooltip(Text.of("A simple toggle button."))
+ .tooltip(value -> Text.of("A simple toggle button that contains the value '" + value + "'"))
.binding(
false,
() -> TestSettings.booleanToggle,
@@ -190,6 +192,10 @@ public class ModMenuIntegration implements ModMenuApi {
.build())
.build())
.build())
+ .category(PlaceholderCategory.createBuilder()
+ .name(Text.of("Placeholder Category"))
+ .screen((client, yaclScreen) -> new RequireRestartScreen(yaclScreen))
+ .build())
.category(ConfigCategory.createBuilder()
.name(Text.of("Group Test"))
.option(Option.createBuilder(boolean.class)