aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/dev
diff options
context:
space:
mode:
authorxander <xander@isxander.dev>2022-09-01 08:57:59 +0100
committerxander <xander@isxander.dev>2022-09-01 08:57:59 +0100
commit6f8ef7daaafd71090b2c334c10eadc8dedc738d9 (patch)
treed4054a65d99070c944132be83d25e109750dc5f9 /src/main/java/dev
parent9d0a5e937f97c1c17d034393e01636d5241f376a (diff)
downloadYetAnotherConfigLib-6f8ef7daaafd71090b2c334c10eadc8dedc738d9.tar.gz
YetAnotherConfigLib-6f8ef7daaafd71090b2c334c10eadc8dedc738d9.tar.bz2
YetAnotherConfigLib-6f8ef7daaafd71090b2c334c10eadc8dedc738d9.zip
GUI Implementation
Added groups Added button "option" Added test mod
Diffstat (limited to 'src/main/java/dev')
-rw-r--r--src/main/java/dev/isxander/yacl/api/Binding.java2
-rw-r--r--src/main/java/dev/isxander/yacl/api/ButtonOption.java75
-rw-r--r--src/main/java/dev/isxander/yacl/api/ConfigCategory.java31
-rw-r--r--src/main/java/dev/isxander/yacl/api/Control.java8
-rw-r--r--src/main/java/dev/isxander/yacl/api/NameableEnum.java7
-rw-r--r--src/main/java/dev/isxander/yacl/api/Option.java21
-rw-r--r--src/main/java/dev/isxander/yacl/api/OptionGroup.java52
-rw-r--r--src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java31
-rw-r--r--src/main/java/dev/isxander/yacl/api/utils/Dimension.java21
-rw-r--r--src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java28
-rw-r--r--src/main/java/dev/isxander/yacl/gui/AbstractWidget.java45
-rw-r--r--src/main/java/dev/isxander/yacl/gui/OptionListWidget.java90
-rw-r--r--src/main/java/dev/isxander/yacl/gui/YACLScreen.java150
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ActionControl.java58
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ControlWidget.java125
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/EnumControl.java77
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/TickBoxControl.java100
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/DoubleSliderControl.java72
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/FloatSliderControl.java72
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/ISliderControl.java29
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/IntegerSliderControl.java72
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControlElement.java125
-rw-r--r--src/main/java/dev/isxander/yacl/impl/ButtonOptionImpl.java105
-rw-r--r--src/main/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java4
-rw-r--r--src/main/java/dev/isxander/yacl/impl/GenericBindingImpl.java5
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java10
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionImpl.java39
-rw-r--r--src/main/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java9
-rw-r--r--src/main/java/dev/isxander/yacl/impl/utils/DimensionIntegerImpl.java96
29 files changed, 1520 insertions, 39 deletions
diff --git a/src/main/java/dev/isxander/yacl/api/Binding.java b/src/main/java/dev/isxander/yacl/api/Binding.java
index 67ff822..74120a0 100644
--- a/src/main/java/dev/isxander/yacl/api/Binding.java
+++ b/src/main/java/dev/isxander/yacl/api/Binding.java
@@ -10,7 +10,7 @@ public interface Binding<T> {
T getValue();
- void resetValue();
+ T defaultValue();
static <T> Binding<T> of(T def, Supplier<T> getter, Consumer<T> setter) {
return new GenericBindingImpl<>(def, getter, setter);
diff --git a/src/main/java/dev/isxander/yacl/api/ButtonOption.java b/src/main/java/dev/isxander/yacl/api/ButtonOption.java
new file mode 100644
index 0000000..08436b3
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/api/ButtonOption.java
@@ -0,0 +1,75 @@
+package dev.isxander.yacl.api;
+
+import dev.isxander.yacl.impl.ButtonOptionImpl;
+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.Function;
+
+public interface ButtonOption extends Option<Runnable> {
+ Runnable action();
+
+ static Builder createBuilder() {
+ return new Builder();
+ }
+
+ class Builder {
+ private Text name;
+ private final List<Text> tooltipLines = new ArrayList<>();
+ private Function<ButtonOption, Control<Runnable>> controlGetter;
+ private Runnable action;
+
+ private Builder() {
+
+ }
+
+ public Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` cannot be null");
+
+ this.name = name;
+ return this;
+ }
+
+ public Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ public Builder action(@NotNull Runnable action) {
+ Validate.notNull(action, "`action` cannot be null");
+
+ this.action = action;
+ return this;
+ }
+
+ public Builder controller(@NotNull Function<ButtonOption, Control<Runnable>> control) {
+ Validate.notNull(control, "`control` cannot be null");
+
+ this.controlGetter = control;
+ return this;
+ }
+
+ public ButtonOption build() {
+ Validate.notNull(name, "`name` must not be null when building `Option`");
+ Validate.notNull(controlGetter, "`control` must not be null when building `Option`");
+ Validate.notNull(action, "`action` 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;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new ButtonOptionImpl(name, concatenatedTooltip, action, controlGetter);
+ }
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
index ee2fbc7..13552d6 100644
--- a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
+++ b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
@@ -1,18 +1,22 @@
package dev.isxander.yacl.api;
import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.gui.YACLScreen;
import dev.isxander.yacl.impl.ConfigCategoryImpl;
+import dev.isxander.yacl.impl.OptionGroupImpl;
+import net.minecraft.client.gui.widget.ClickableWidget;
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.Function;
public interface ConfigCategory {
@NotNull Text name();
- @NotNull ImmutableList<Option<?>> options();
+ @NotNull ImmutableList<OptionGroup> groups();
static Builder createBuilder() {
return new Builder();
@@ -20,31 +24,44 @@ public interface ConfigCategory {
class Builder {
private Text name;
- private final List<Option<?>> options = new ArrayList<>();
+ private final List<Option<?>> rootOptions = new ArrayList<>();
+
+ private final List<OptionGroup> groups = new ArrayList<>();
private Builder() {
}
- public Builder setName(@NotNull Text name) {
+ public Builder name(@NotNull Text name) {
Validate.notNull(name, "`name` cannot be null");
this.name = name;
return this;
}
- public Builder addOption(@NotNull Option<?> option) {
+ public Builder option(@NotNull Option<?> option) {
Validate.notNull(option, "`option` must not be null");
- this.options.add(option);
+ this.rootOptions.add(option);
+ return this;
+ }
+
+ public Builder group(@NotNull OptionGroup group) {
+ Validate.notNull(group, "`group` must not be null");
+
+ this.groups.add(group);
return this;
}
public ConfigCategory build() {
Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
- Validate.notEmpty(options, "`at least one option must be added to build `ConfigCategory`");
+ Validate.notEmpty(rootOptions, "`at least one option must be added to build `ConfigCategory`");
+
+ List<OptionGroup> combinedGroups = new ArrayList<>();
+ combinedGroups.add(new OptionGroupImpl(Text.empty(), ImmutableList.copyOf(rootOptions), true));
+ combinedGroups.addAll(groups);
- return new ConfigCategoryImpl(name, ImmutableList.copyOf(options));
+ return new ConfigCategoryImpl(name, ImmutableList.copyOf(combinedGroups));
}
}
}
diff --git a/src/main/java/dev/isxander/yacl/api/Control.java b/src/main/java/dev/isxander/yacl/api/Control.java
index 0733c4f..242b2c8 100644
--- a/src/main/java/dev/isxander/yacl/api/Control.java
+++ b/src/main/java/dev/isxander/yacl/api/Control.java
@@ -1,10 +1,14 @@
package dev.isxander.yacl.api;
import dev.isxander.yacl.api.utils.Dimension;
-import dev.isxander.yacl.gui.AbstractWidget;
+import dev.isxander.yacl.gui.controllers.ControlWidget;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
public interface Control<T> {
Option<T> option();
- AbstractWidget provideWidget(Dimension<Integer> widgetDimension);
+ Text formatValue();
+
+ ControlWidget<?> provideWidget(Screen screen, Dimension<Integer> widgetDimension);
}
diff --git a/src/main/java/dev/isxander/yacl/api/NameableEnum.java b/src/main/java/dev/isxander/yacl/api/NameableEnum.java
new file mode 100644
index 0000000..12a58c3
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/api/NameableEnum.java
@@ -0,0 +1,7 @@
+package dev.isxander.yacl.api;
+
+import net.minecraft.text.Text;
+
+public interface NameableEnum {
+ Text getDisplayName();
+}
diff --git a/src/main/java/dev/isxander/yacl/api/Option.java b/src/main/java/dev/isxander/yacl/api/Option.java
index 1c7a8a1..5a98d50 100644
--- a/src/main/java/dev/isxander/yacl/api/Option.java
+++ b/src/main/java/dev/isxander/yacl/api/Option.java
@@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
public interface Option<T> {
@@ -23,11 +24,17 @@ public interface Option<T> {
boolean changed();
+ T pendingValue();
+
void requestSet(T value);
void applyValue();
- static <T> Builder<T> createBuilder() {
+ void forgetPendingValue();
+
+ void requestSetDefault();
+
+ static <T> Builder<T> createBuilder(Class<T> clazz) {
return new Builder<>();
}
@@ -36,7 +43,7 @@ public interface Option<T> {
private final List<Text> tooltipLines = new ArrayList<>();
- private Control<T> control;
+ private Function<Option<T>, Control<T>> controlGetter;
private Binding<T> binding;
@@ -58,10 +65,10 @@ public interface Option<T> {
return this;
}
- public Builder<T> controller(@NotNull Control<T> control) {
+ public Builder<T> controller(@NotNull Function<Option<T>, Control<T>> control) {
Validate.notNull(control, "`control` cannot be null");
- this.control = control;
+ this.controlGetter = control;
return this;
}
@@ -73,7 +80,7 @@ public interface Option<T> {
}
public Builder<T> binding(@NotNull T def, @NotNull Supplier<@NotNull T> getter, @NotNull Consumer<@NotNull T> setter) {
- Validate.notNull(def, "`default` must not be null");
+ Validate.notNull(def, "`def` must not be null");
Validate.notNull(getter, "`getter` must not be null");
Validate.notNull(setter, "`setter` must not be null");
@@ -83,7 +90,7 @@ public interface Option<T> {
public Option<T> build() {
Validate.notNull(name, "`name` must not be null when building `Option`");
- Validate.notNull(control, "`control` must not be null when building `Option`");
+ 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();
@@ -95,7 +102,7 @@ public interface Option<T> {
concatenatedTooltip.append(line);
}
- return new OptionImpl<>(name, concatenatedTooltip, control, binding);
+ return new OptionImpl<>(name, concatenatedTooltip, controlGetter, binding);
}
}
}
diff --git a/src/main/java/dev/isxander/yacl/api/OptionGroup.java b/src/main/java/dev/isxander/yacl/api/OptionGroup.java
new file mode 100644
index 0000000..bedbc82
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/api/OptionGroup.java
@@ -0,0 +1,52 @@
+package dev.isxander.yacl.api;
+
+import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.impl.OptionGroupImpl;
+import net.minecraft.text.Text;
+import org.apache.commons.lang3.Validate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public interface OptionGroup {
+ Text name();
+
+ @NotNull ImmutableList<Option<?>> options();
+
+ boolean isRoot();
+
+ static Builder createBuilder() {
+ return new Builder();
+ }
+
+ class Builder {
+ private Text name = Text.empty();
+ private final List<Option<?>> options = new ArrayList<>();
+
+ private Builder() {
+
+ }
+
+ public Builder name(@NotNull Text name) {
+ Validate.notNull(name, "`name` must not be null");
+
+ this.name = name;
+ return this;
+ }
+
+ public Builder option(@NotNull Option<?> option) {
+ Validate.notNull(option, "`option` must not be null");
+
+ this.options.add(option);
+ return this;
+ }
+
+ public OptionGroup build() {
+ Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`");
+
+ return new OptionGroupImpl(name, ImmutableList.copyOf(options), false);
+ }
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java b/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
index d7f8416..a598e27 100644
--- a/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
+++ b/src/main/java/dev/isxander/yacl/api/YetAnotherConfigLib.java
@@ -1,14 +1,17 @@
package dev.isxander.yacl.api;
import com.google.common.collect.ImmutableList;
+import dev.isxander.yacl.gui.YACLScreen;
import dev.isxander.yacl.impl.YetAnotherConfigLibImpl;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
public interface YetAnotherConfigLib {
@@ -16,7 +19,11 @@ public interface YetAnotherConfigLib {
ImmutableList<ConfigCategory> categories();
- Screen generateScreen();
+ Runnable saveFunction();
+
+ Consumer<YACLScreen> initConsumer();
+
+ Screen generateScreen(@Nullable Screen parent);
static Builder createBuilder(Text title) {
return new Builder(title);
@@ -25,31 +32,47 @@ public interface YetAnotherConfigLib {
class Builder {
private Text title;
private final List<ConfigCategory> categories = new ArrayList<>();
+ private Runnable saveFunction = () -> {};
+ private Consumer<YACLScreen> initConsumer = screen -> {};
private Builder(@NotNull Text title) {
Validate.notNull(title, "`title` cannot be null");
this.title = title;
}
- public Builder setTitle(@NotNull Text title) {
+ public Builder title(@NotNull Text title) {
Validate.notNull(title, "`title` cannot be null");
this.title = title;
return this;
}
- public Builder addCategory(@NotNull ConfigCategory category) {
+ public Builder category(@NotNull ConfigCategory category) {
Validate.notNull(category, "`category` cannot be null");
this.categories.add(category);
return this;
}
+ public Builder save(@NotNull Runnable saveFunction) {
+ Validate.notNull(saveFunction, "`saveFunction` cannot be null");
+
+ this.saveFunction = saveFunction;
+ return this;
+ }
+
+ public Builder screenInit(@NotNull Consumer<YACLScreen> initConsumer) {
+ Validate.notNull(initConsumer, "`initConsumer` cannot be null");
+
+ this.initConsumer = initConsumer;
+ return this;
+ }
+
public YetAnotherConfigLib build() {
Validate.notNull(title, "`title must not be null to build `YetAnotherConfigLib`");
Validate.notEmpty(categories, "`categories` must not be empty to build `YetAnotherConfigLib`");
- return new YetAnotherConfigLibImpl(title, ImmutableList.copyOf(categories));
+ return new YetAnotherConfigLibImpl(title, ImmutableList.copyOf(categories), saveFunction, initConsumer);
}
}
}
diff --git a/src/main/java/dev/isxander/yacl/api/utils/Dimension.java b/src/main/java/dev/isxander/yacl/api/utils/Dimension.java
index cf7127a..69958b1 100644
--- a/src/main/java/dev/isxander/yacl/api/utils/Dimension.java
+++ b/src/main/java/dev/isxander/yacl/api/utils/Dimension.java
@@ -12,8 +12,29 @@ public interface Dimension<T extends Number> {
T xLimit();
T yLimit();
+ T centerX();
+ T centerY();
+
boolean isPointInside(T x, T y);
+ Dimension<T> clone();
+
+ Dimension<T> setX(T x);
+ Dimension<T> setY(T y);
+ Dimension<T> setWidth(T width);
+ Dimension<T> setHeight(T height);
+
+ Dimension<T> withX(T x);
+ Dimension<T> withY(T y);
+ Dimension<T> withWidth(T width);
+ Dimension<T> withHeight(T height);
+
+ Dimension<T> move(T x, T y);
+ Dimension<T> expand(T width, T height);
+
+ Dimension<T> moved(T x, T y);
+ Dimension<T> expanded(T width, T height);
+
static Dimension<Integer> ofInt(int x, int y, int width, int height) {
return new DimensionIntegerImpl(x, y, width, height);
}
diff --git a/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java b/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java
new file mode 100644
index 0000000..ed51683
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/api/utils/OptionUtils.java
@@ -0,0 +1,28 @@
+package dev.isxander.yacl.api.utils;
+
+import dev.isxander.yacl.api.ConfigCategory;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.OptionGroup;
+import dev.isxander.yacl.api.YetAnotherConfigLib;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class OptionUtils {
+ public static void consumeOptions(YetAnotherConfigLib yacl, Function<Option<?>, Boolean> consumer) {
+ for (ConfigCategory category : yacl.categories()) {
+ for (OptionGroup group : category.groups()) {
+ for (Option<?> option : group.options()) {
+ if (!consumer.apply(option)) return;
+ }
+ }
+ }
+ }
+
+ public static void forEachOptions(YetAnotherConfigLib yacl, Consumer<Option<?>> consumer) {
+ consumeOptions(yacl, (opt) -> {
+ consumer.accept(opt);
+ return true;
+ });
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/AbstractWidget.java b/src/main/java/dev/isxander/yacl/gui/AbstractWidget.java
index 2a8a519..7affbd4 100644
--- a/src/main/java/dev/isxander/yacl/gui/AbstractWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/AbstractWidget.java
@@ -1,8 +1,53 @@
package dev.isxander.yacl.gui;
+import com.mojang.blaze3d.systems.RenderSystem;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.Drawable;
+import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable;
+import net.minecraft.client.gui.widget.ClickableWidget;
+import net.minecraft.client.render.GameRenderer;
+import net.minecraft.client.sound.PositionedSoundInstance;
+import net.minecraft.client.sound.SoundManager;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.sound.SoundEvents;
public abstract class AbstractWidget implements Element, Drawable, Selectable {
+ protected final MinecraftClient client = MinecraftClient.getInstance();
+ protected final TextRenderer textRenderer = client.textRenderer;
+
+ public void tick() {
+
+ }
+
+ protected void drawButtonRect(MatrixStack matrices, int x1, int y1, int x2, int y2, boolean hovered) {
+ if (x1 > x2) {
+ int xx1 = x1;
+ x1 = x2;
+ x2 = xx1;
+ }
+ if (y1 > y2) {
+ int yy1 = y1;
+ y1 = y2;
+ y2 = yy1;
+ }
+ int width = x2 - x1;
+ int height = y2 - y1;
+
+ RenderSystem.setShader(GameRenderer::getPositionTexShader);
+ RenderSystem.setShaderTexture(0, ClickableWidget.WIDGETS_TEXTURE);
+ RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
+ int i = hovered ? 2 : 1;
+ RenderSystem.enableBlend();
+ RenderSystem.defaultBlendFunc();
+ RenderSystem.enableDepthTest();
+ DrawableHelper.drawTexture(matrices, x1, y1, 0, 0, 46 + i * 20, width / 2, height, 256, 256);
+ DrawableHelper.drawTexture(matrices, x1 + width / 2, y1, 0, 200 - width / 2f, 46 + i * 20, width / 2, height, 256, 256);
+ }
+
+ public void playDownSound() {
+ MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+ }
}
diff --git a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
new file mode 100644
index 0000000..6cb7090
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
@@ -0,0 +1,90 @@
+package dev.isxander.yacl.gui;
+
+import dev.isxander.yacl.api.ConfigCategory;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.OptionGroup;
+import dev.isxander.yacl.api.utils.Dimension;
+import dev.isxander.yacl.gui.controllers.ControlWidget;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.Selectable;
+import net.minecraft.client.gui.widget.ElementListWidget;
+import net.minecraft.client.util.math.MatrixStack;
+
+import java.util.List;
+
+public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry> {
+
+ public OptionListWidget(ConfigCategory category, YACLScreen screen, MinecraftClient client, int width, int height) {
+ super(client, width / 3 * 2, height, 0, height, 22);
+ left = width - this.width;
+ right = width;
+
+ for (OptionGroup group : category.groups()) {
+ if (!group.isRoot())
+ addEntry(new GroupSeparatorEntry(group));
+ for (Option<?> option : group.options()) {
+ addEntry(new OptionEntry(option.control().provideWidget(screen, null)));
+ }
+ }
+ }
+
+ @Override
+ protected int getScrollbarPositionX() {
+ return left + super.getScrollbarPositionX();
+ }
+
+ public static abstract class Entry extends ElementListWidget.Entry<Entry> {
+
+ }
+
+ private static class OptionEntry extends Entry {
+ private final ControlWidget<?> widget;
+
+ public OptionEntry(ControlWidget<?> widget) {
+ this.widget = widget;
+ }
+
+ @Override
+ public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
+ widget.dim = Dimension.ofInt(x, y, entryWidth, 20);
+
+ widget.render(matrices, mouseX, mouseY, tickDelta);
+ }
+
+ @Override
+ public List<? extends Selectable> selectableChildren() {
+ return List.of(widget);
+ }
+
+ @Override
+ public List<? extends Element> children() {
+ return List.of(widget);
+ }
+ }
+
+ private static class GroupSeparatorEntry extends Entry {
+ private final OptionGroup group;
+
+ public GroupSeparatorEntry(OptionGroup group) {
+ this.group = group;
+ }
+
+ @Override
+ public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
+ TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+ drawCenteredText(matrices, textRenderer, group.name(), x + entryWidth / 2, y + entryHeight / 2 - textRenderer.fontHeight / 2, -1);
+ }
+
+ @Override
+ public List<? extends Selectable> selectableChildren() {
+ return List.of();
+ }
+
+ @Override
+ public List<? extends Element> children() {
+ return List.of();
+ }
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
new file mode 100644
index 0000000..97cc8ed
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
@@ -0,0 +1,150 @@
+package dev.isxander.yacl.gui;
+
+import dev.isxander.yacl.api.ConfigCategory;
+import dev.isxander.yacl.api.Option;
+import dev.isxander.yacl.api.YetAnotherConfigLib;
+import dev.isxander.yacl.api.utils.Dimension;
+import dev.isxander.yacl.api.utils.OptionUtils;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class YACLScreen extends Screen {
+ public final YetAnotherConfigLib config;
+ public int currentCategoryIdx;
+
+ private final Screen parent;
+
+ public OptionListWidget optionList;
+ public final List<ButtonWidget> categoryButtons;
+ public ButtonWidget finishedSaveButton, cancelResetButton, undoButton;
+
+ public YACLScreen(YetAnotherConfigLib config, Screen parent) {
+ 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;
+ Dimension<Integer> categoryDim = Dimension.ofInt(padding, padding, columnWidth - padding * 2, 20);
+ int idx = 0;
+ for (ConfigCategory category : config.categories()) {
+ ButtonWidget categoryWidget = new ButtonWidget(
+ categoryDim.x(), categoryDim.y(),
+ categoryDim.width(), categoryDim.height(),
+ category.name(),
+ (btn) -> changeCategory(categoryButtons.indexOf(btn))
+ );
+ if (idx == currentCategoryIdx)
+ categoryWidget.active = false;
+ categoryButtons.add(categoryWidget);