From 51f4ae2d8e0a6cdcc7d50a037143f48a6132214a Mon Sep 17 00:00:00 2001 From: isXander Date: Wed, 14 Dec 2022 18:53:30 +0000 Subject: lots of minor fixes with lists and abstract builders --- .../dev/isxander/yacl/impl/ButtonOptionImpl.java | 83 ++++++++++- .../dev/isxander/yacl/impl/ConfigCategoryImpl.java | 98 +++++++++++++ .../dev/isxander/yacl/impl/GenericBindingImpl.java | 2 +- .../isxander/yacl/impl/ListOptionEntryImpl.java | 33 ++++- .../dev/isxander/yacl/impl/ListOptionImpl.java | 130 ++++++++++++++++- .../dev/isxander/yacl/impl/OptionGroupImpl.java | 76 ++++++++++ .../java/dev/isxander/yacl/impl/OptionImpl.java | 156 ++++++++++++++++++++- .../yacl/impl/PlaceholderCategoryImpl.java | 56 ++++++++ .../yacl/impl/YetAnotherConfigLibImpl.java | 83 ++++++++--- 9 files changed, 679 insertions(+), 38 deletions(-) (limited to 'src/client/java/dev/isxander/yacl/impl') diff --git a/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java index f526d42..25260c4 100644 --- a/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java @@ -3,14 +3,21 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableSet; import dev.isxander.yacl.api.*; import dev.isxander.yacl.gui.YACLScreen; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import org.apache.commons.lang3.Validate; +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.Consumer; import java.util.function.Function; -public class ButtonOptionImpl implements ButtonOption { +@ApiStatus.Internal +public final class ButtonOptionImpl implements ButtonOption { private final Text name; private final Text tooltip; private final BiConsumer action; @@ -134,4 +141,78 @@ public class ButtonOptionImpl implements ButtonOption { throw new UnsupportedOperationException(); } } + + @ApiStatus.Internal + public static final class BuilderImpl implements ButtonOption.Builder { + private Text name; + private final List tooltipLines = new ArrayList<>(); + private boolean available = true; + private Function>> controlGetter; + private BiConsumer action; + + @Override + public ButtonOption.Builder name(@NotNull Text name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public ButtonOption.Builder tooltip(@NotNull Text... tooltips) { + Validate.notNull(tooltips, "`tooltips` cannot be empty"); + + tooltipLines.addAll(List.of(tooltips)); + return this; + } + + @Override + public ButtonOption.Builder action(@NotNull BiConsumer action) { + Validate.notNull(action, "`action` cannot be null"); + + this.action = action; + return this; + } + + @Override + @Deprecated + public ButtonOption.Builder action(@NotNull Consumer action) { + Validate.notNull(action, "`action` cannot be null"); + + this.action = (screen, button) -> action.accept(screen); + return this; + } + + @Override + public ButtonOption.Builder available(boolean available) { + this.available = available; + return this; + } + + @Override + public ButtonOption.Builder controller(@NotNull Function>> control) { + Validate.notNull(control, "`control` cannot be null"); + + this.controlGetter = control; + return this; + } + + @Override + 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, available, controlGetter); + } + } } diff --git a/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java b/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java index 971fecf..b3da814 100644 --- a/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/ConfigCategoryImpl.java @@ -2,9 +2,107 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; import dev.isxander.yacl.api.ConfigCategory; +import dev.isxander.yacl.api.ListOption; +import dev.isxander.yacl.api.Option; import dev.isxander.yacl.api.OptionGroup; +import dev.isxander.yacl.impl.utils.YACLConstants; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@ApiStatus.Internal public record ConfigCategoryImpl(Text name, ImmutableList groups, Text tooltip) implements ConfigCategory { + @ApiStatus.Internal + public static final class BuilderImpl implements ConfigCategory.Builder { + private Text name; + + private final List> rootOptions = new ArrayList<>(); + private final List groups = new ArrayList<>(); + + private final List tooltipLines = new ArrayList<>(); + + @Override + public ConfigCategory.Builder name(@NotNull Text name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public ConfigCategory.Builder option(@NotNull Option option) { + Validate.notNull(option, "`option` must not be null"); + + if (option instanceof ListOption listOption) { + YACLConstants.LOGGER.warn("Adding list option as an option is not supported! Rerouting to group!"); + return group(listOption); + } + + this.rootOptions.add(option); + return this; + } + + @Override + public ConfigCategory.Builder options(@NotNull Collection> options) { + Validate.notNull(options, "`options` must not be null"); + + if (options.stream().anyMatch(ListOption.class::isInstance)) + throw new UnsupportedOperationException("List options must not be added as an option but a group!"); + + this.rootOptions.addAll(options); + return this; + } + + @Override + public ConfigCategory.Builder group(@NotNull OptionGroup group) { + Validate.notNull(group, "`group` must not be null"); + + this.groups.add(group); + return this; + } + + @Override + public ConfigCategory.Builder groups(@NotNull Collection groups) { + Validate.notEmpty(groups, "`groups` must not be empty"); + + this.groups.addAll(groups); + return this; + } + + @Override + public ConfigCategory.Builder tooltip(@NotNull Text... tooltips) { + Validate.notEmpty(tooltips, "`tooltips` cannot be empty"); + + tooltipLines.addAll(List.of(tooltips)); + return this; + } + + @Override + public ConfigCategory build() { + Validate.notNull(name, "`name` must not be null to build `ConfigCategory`"); + + List combinedGroups = new ArrayList<>(); + combinedGroups.add(new OptionGroupImpl(Text.empty(), Text.empty(), ImmutableList.copyOf(rootOptions), false, true)); + combinedGroups.addAll(groups); + + Validate.notEmpty(combinedGroups, "at least one option must be added 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 ConfigCategoryImpl(name, ImmutableList.copyOf(combinedGroups), concatenatedTooltip); + } + } } diff --git a/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java b/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java index 1867bb6..0d668c6 100644 --- a/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/GenericBindingImpl.java @@ -5,7 +5,7 @@ import dev.isxander.yacl.api.Binding; import java.util.function.Consumer; import java.util.function.Supplier; -public class GenericBindingImpl implements Binding { +public final class GenericBindingImpl implements Binding { private final T def; private final Supplier getter; private final Consumer setter; diff --git a/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java b/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java index dc7aa88..98188dc 100644 --- a/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java @@ -1,13 +1,19 @@ package dev.isxander.yacl.impl; import dev.isxander.yacl.api.*; +import dev.isxander.yacl.api.utils.Dimension; +import dev.isxander.yacl.gui.AbstractWidget; +import dev.isxander.yacl.gui.YACLScreen; +import dev.isxander.yacl.gui.controllers.ListEntryWidget; import net.minecraft.text.Text; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.util.function.BiConsumer; import java.util.function.Function; -public class ListOptionEntryImpl implements ListOptionEntry { +@ApiStatus.Internal +public final class ListOptionEntryImpl implements ListOptionEntry { private final ListOptionImpl group; private T value; @@ -15,11 +21,11 @@ public class ListOptionEntryImpl implements ListOptionEntry { private final Binding binding; private final Controller controller; - public ListOptionEntryImpl(ListOptionImpl group, T initialValue, @NotNull Function, Controller> controlGetter) { + ListOptionEntryImpl(ListOptionImpl group, T initialValue, @NotNull Function, Controller> controlGetter) { this.group = group; this.value = initialValue; this.binding = new EntryBinding(); - this.controller = controlGetter.apply(this); + this.controller = new EntryController<>(controlGetter.apply(this), this); } @Override @@ -102,6 +108,27 @@ public class ListOptionEntryImpl implements ListOptionEntry { } + /** + * Open in case mods need to find the real controller type. + */ + @ApiStatus.Internal + public record EntryController(Controller controller, ListOptionEntryImpl entry) implements Controller { + @Override + public Option option() { + return controller.option(); + } + + @Override + public Text formatValue() { + return controller.formatValue(); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension widgetDimension) { + return new ListEntryWidget(screen, entry, controller.provideWidget(screen, widgetDimension)); + } + } + private class EntryBinding implements Binding { @Override public void setValue(T newValue) { diff --git a/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java b/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java index 128d3e7..1924205 100644 --- a/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java @@ -3,17 +3,21 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import dev.isxander.yacl.api.*; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +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.Collectors; -public class ListOptionImpl implements ListOption { +@ApiStatus.Internal +public final class ListOptionImpl implements ListOption { private final Text name; private final Text tooltip; private final Binding> binding; @@ -109,8 +113,8 @@ public class ListOptionImpl implements ListOption { @Override public void removeEntry(ListOptionEntry entry) { - entries.remove(entry); - onRefresh(); + if (entries.remove(entry)) + onRefresh(); } @Override @@ -123,7 +127,6 @@ public class ListOptionImpl implements ListOption { entries.clear(); entries.addAll(createEntries(value)); onRefresh(); - listeners.forEach(listener -> listener.accept(this, value)); } @Override @@ -205,4 +208,117 @@ public class ListOptionImpl implements ListOption { return new ListOptionEntryImpl<>(ListOptionImpl.this, initialValue, controllerFunction); } } + + @ApiStatus.Internal + public static final class BuilderImpl implements ListOption.Builder { + private Text name = Text.empty(); + private final List tooltipLines = new ArrayList<>(); + private Function, Controller> controllerFunction; + private Binding> binding = null; + private final Set flags = new HashSet<>(); + private T initialValue; + private boolean collapsed = false; + private boolean available = true; + private final Class typeClass; + + public BuilderImpl(Class typeClass) { + this.typeClass = typeClass; + } + + @Override + public ListOption.Builder name(@NotNull Text name) { + Validate.notNull(name, "`name` must not be null"); + + this.name = name; + return this; + } + + @Override + public ListOption.Builder tooltip(@NotNull Text... tooltips) { + Validate.notEmpty(tooltips, "`tooltips` cannot be empty"); + + tooltipLines.addAll(List.of(tooltips)); + return this; + } + + @Override + public ListOption.Builder initial(@NotNull T initialValue) { + Validate.notNull(initialValue, "`initialValue` cannot be empty"); + + this.initialValue = initialValue; + return this; + } + + @Override + public ListOption.Builder controller(@NotNull Function, Controller> control) { + Validate.notNull(control, "`control` cannot be null"); + + this.controllerFunction = control; + return this; + } + + @Override + public ListOption.Builder binding(@NotNull Binding> binding) { + Validate.notNull(binding, "`binding` cannot be null"); + + this.binding = binding; + return this; + } + + @Override + public ListOption.Builder binding(@NotNull List def, @NotNull Supplier<@NotNull List> getter, @NotNull Consumer<@NotNull List> setter) { + Validate.notNull(def, "`def` must not be null"); + Validate.notNull(getter, "`getter` must not be null"); + Validate.notNull(setter, "`setter` must not be null"); + + this.binding = Binding.generic(def, getter, setter); + return this; + } + + @Override + public ListOption.Builder available(boolean available) { + this.available = available; + return this; + } + + @Override + public ListOption.Builder flag(@NotNull OptionFlag... flag) { + Validate.notNull(flag, "`flag` must not be null"); + + this.flags.addAll(Arrays.asList(flag)); + return this; + } + + @Override + public ListOption.Builder flags(@NotNull Collection flags) { + Validate.notNull(flags, "`flags` must not be null"); + + this.flags.addAll(flags); + return this; + } + + @Override + public ListOption.Builder collapsed(boolean collapsible) { + this.collapsed = collapsible; + return this; + } + + @Override + public ListOption build() { + Validate.notNull(controllerFunction, "`controller` must not be null"); + Validate.notNull(binding, "`binding` must not be null"); + Validate.notNull(initialValue, "`initialValue` must not be null"); + + MutableText concatenatedTooltip = Text.empty(); + boolean first = true; + for (Text line : tooltipLines) { + if (!first) concatenatedTooltip.append("\n"); + first = false; + + concatenatedTooltip.append(line); + } + + return new ListOptionImpl<>(name, concatenatedTooltip, binding, initialValue, typeClass, controllerFunction, ImmutableSet.copyOf(flags), collapsed, available); + } + } } diff --git a/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java b/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java index 02ef04c..89a2adf 100644 --- a/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java @@ -1,10 +1,86 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; +import dev.isxander.yacl.api.ListOption; import dev.isxander.yacl.api.Option; import dev.isxander.yacl.api.OptionGroup; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@ApiStatus.Internal public record OptionGroupImpl(@NotNull Text name, @NotNull Text tooltip, ImmutableList> options, boolean collapsed, boolean isRoot) implements OptionGroup { + @ApiStatus.Internal + public static final class BuilderImpl implements OptionGroup.Builder { + private Text name = Text.empty(); + private final List tooltipLines = new ArrayList<>(); + private final List> options = new ArrayList<>(); + private boolean collapsed = false; + + @Override + public OptionGroup.Builder name(@NotNull Text name) { + Validate.notNull(name, "`name` must not be null"); + + this.name = name; + return this; + } + + @Override + public OptionGroup.Builder tooltip(@NotNull Text... tooltips) { + Validate.notEmpty(tooltips, "`tooltips` cannot be empty"); + + tooltipLines.addAll(List.of(tooltips)); + return this; + } + + @Override + public OptionGroup.Builder option(@NotNull Option option) { + Validate.notNull(option, "`option` must not be null"); + + if (option instanceof ListOption) + throw new UnsupportedOperationException("List options must not be added as an option but a group!"); + + this.options.add(option); + return this; + } + + @Override + public OptionGroup.Builder options(@NotNull Collection> options) { + Validate.notEmpty(options, "`options` must not be empty"); + + if (options.stream().anyMatch(ListOption.class::isInstance)) + throw new UnsupportedOperationException("List options must not be added as an option but a group!"); + + this.options.addAll(options); + return this; + } + + @Override + public OptionGroup.Builder collapsed(boolean collapsible) { + this.collapsed = collapsible; + return this; + } + + @Override + public OptionGroup build() { + Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`"); + + MutableText concatenatedTooltip = Text.empty(); + boolean first = true; + for (Text line : tooltipLines) { + if (!first) concatenatedTooltip.append("\n"); + first = false; + + concatenatedTooltip.append(line); + } + + return new OptionGroupImpl(name, concatenatedTooltip, ImmutableList.copyOf(options), collapsed, false); + } + } } diff --git a/src/client/java/dev/isxander/yacl/impl/OptionImpl.java b/src/client/java/dev/isxander/yacl/impl/OptionImpl.java index 0cc156f..d2815e1 100644 --- a/src/client/java/dev/isxander/yacl/impl/OptionImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/OptionImpl.java @@ -5,17 +5,23 @@ import dev.isxander.yacl.api.Binding; import dev.isxander.yacl.api.Controller; import dev.isxander.yacl.api.Option; import dev.isxander.yacl.api.OptionFlag; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +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 class OptionImpl implements Option { +@ApiStatus.Internal +public final class OptionImpl implements Option { private final Text name; private Text tooltip; private final Controller controller; @@ -136,4 +142,146 @@ public class OptionImpl implements Option { public void addListener(BiConsumer, T> changedListener) { this.listeners.add(changedListener); } + + @ApiStatus.Internal + public static class BuilderImpl implements Option.Builder { + private Text name = Text.literal("Name not specified!").formatted(Formatting.RED); + + private final List> tooltipGetters = new ArrayList<>(); + + private Function, Controller> controlGetter; + + private Binding binding; + + private boolean available = true; + + private boolean instant = false; + + private final Set flags = new HashSet<>(); + + private final Class typeClass; + + private final List, T>> listeners = new ArrayList<>(); + + public BuilderImpl(Class typeClass) { + this.typeClass = typeClass; + } + + @Override + public Option.Builder name(@NotNull Text name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + @SafeVarargs + public final Option.Builder tooltip(@NotNull Function... tooltipGetter) { + Validate.notNull(tooltipGetter, "`tooltipGetter` cannot be null"); + + this.tooltipGetters.addAll(List.of(tooltipGetter)); + return this; + } + + @Override + public Option.Builder tooltip(@NotNull Text... tooltips) { + Validate.notNull(tooltips, "`tooltips` cannot be empty"); + + this.tooltipGetters.addAll(Stream.of(tooltips).map(text -> (Function) t -> text).toList()); + return this; + } + + @Override + public Option.Builder controller(@NotNull Function, Controller> control) { + Validate.notNull(control, "`control` cannot be null"); + + this.controlGetter = control; + return this; + } + + @Override + public Option.Builder binding(@NotNull Binding binding) { + Validate.notNull(binding, "`binding` cannot be null"); + + this.binding = binding; + return this; + } + + @Override + public Option.Builder binding(@NotNull T def, @NotNull Supplier<@NotNull T> getter, @NotNull Consumer<@NotNull T> setter) { + Validate.notNull(def, "`def` must not be null"); + Validate.notNull(getter, "`getter` must not be null"); + Validate.notNull(setter, "`setter` must not be null"); + + this.binding = Binding.generic(def, getter, setter); + return this; + } + + @Override + public Option.Builder available(boolean available) { + this.available = available; + return this; + } + + @Override + public Option.Builder flag(@NotNull OptionFlag... flag) { + Validate.notNull(flag, "`flag` must not be null"); + + this.flags.addAll(Arrays.asList(flag)); + return this; + } + + @Override + public Option.Builder flags(@NotNull Collection flags) { + Validate.notNull(flags, "`flags` must not be null"); + + this.flags.addAll(flags); + return this; + } + + @Override + public Option.Builder instant(boolean instant) { + this.instant = instant; + return this; + } + + @Override + public Option.Builder listener(@NotNull BiConsumer, T> listener) { + this.listeners.add(listener); + return this; + } + + @Override + public Option.Builder listeners(@NotNull Collection, T>> listeners) { + this.listeners.addAll(listeners); + return this; + } + + @Override + public Option build() { + Validate.notNull(controlGetter, "`control` must not be null when building `Option`"); + Validate.notNull(binding, "`binding` must not be null when building `Option`"); + Validate.isTrue(!instant || flags.isEmpty(), "instant application does not support option flags"); + + Function concatenatedTooltipGetter = value -> { + MutableText concatenatedTooltip = Text.empty(); + boolean first = true; + for (Function line : tooltipGetters) { + if (!first) concatenatedTooltip.append("\n"); + first = false; + + concatenatedTooltip.append(line.apply(value)); + } + + return concatenatedTooltip; + }; + + if (instant) { + listeners.add((opt, pendingValue) -> opt.applyValue()); + } + + return new OptionImpl<>(name, concatenatedTooltipGetter, controlGetter, binding, available, ImmutableSet.copyOf(flags), typeClass, listeners); + } + } } diff --git a/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java b/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java index a5180ad..28e5886 100644 --- a/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/PlaceholderCategoryImpl.java @@ -1,19 +1,75 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; +import dev.isxander.yacl.api.ConfigCategory; 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.MutableText; import net.minecraft.text.Text; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.function.BiFunction; +@ApiStatus.Internal public record PlaceholderCategoryImpl(Text name, BiFunction screen, Text tooltip) implements PlaceholderCategory { @Override public @NotNull ImmutableList groups() { return ImmutableList.of(); } + + @ApiStatus.Internal + public static final class BuilderImpl implements PlaceholderCategory.Builder { + private Text name; + + private final List tooltipLines = new ArrayList<>(); + + private BiFunction screenFunction; + + @Override + public PlaceholderCategory.Builder name(@NotNull Text name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public PlaceholderCategory.Builder tooltip(@NotNull Text... tooltips) { + Validate.notEmpty(tooltips, "`tooltips` cannot be empty"); + + tooltipLines.addAll(List.of(tooltips)); + return this; + } + + @Override + public PlaceholderCategory.Builder screen(@NotNull BiFunction screenFunction) { + Validate.notNull(screenFunction, "`screenFunction` cannot be null"); + + this.screenFunction = screenFunction; + return this; + } + + @Override + 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/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java b/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java index 380929c..576c1bf 100644 --- a/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java @@ -2,15 +2,23 @@ package dev.isxander.yacl.impl; import com.google.common.collect.ImmutableList; import dev.isxander.yacl.api.ConfigCategory; +import dev.isxander.yacl.api.PlaceholderCategory; import dev.isxander.yacl.api.YetAnotherConfigLib; import dev.isxander.yacl.gui.YACLScreen; import dev.isxander.yacl.impl.utils.YACLConstants; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Text; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.function.Consumer; +@ApiStatus.Internal public final class YetAnotherConfigLibImpl implements YetAnotherConfigLib { private final Text title; private final ImmutableList categories; @@ -56,29 +64,60 @@ public final class YetAnotherConfigLibImpl implements YetAnotherConfigLib { return initConsumer; } - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (YetAnotherConfigLibImpl) obj; - return Objects.equals(this.title, that.title) && - Objects.equals(this.categories, that.categories) && - Objects.equals(this.saveFunction, that.saveFunction) && - Objects.equals(this.initConsumer, that.initConsumer); - } + @ApiStatus.Internal + public static final class BuilderImpl implements YetAnotherConfigLib.Builder { + private Text title; + private final List categories = new ArrayList<>(); + private Runnable saveFunction = () -> {}; + private Consumer initConsumer = screen -> {}; - @Override - public int hashCode() { - return Objects.hash(title, categories, saveFunction, initConsumer); - } + @Override + public YetAnotherConfigLib.Builder title(@NotNull Text title) { + Validate.notNull(title, "`title` cannot be null"); - @Override - public String toString() { - return "YetAnotherConfigLibImpl[" + - "title=" + title + ", " + - "categories=" + categories + ", " + - "saveFunction=" + saveFunction + ", " + - "initConsumer=" + initConsumer + ']'; - } + this.title = title; + return this; + } + + @Override + public YetAnotherConfigLib.Builder category(@NotNull ConfigCategory category) { + Validate.notNull(category, "`category` cannot be null"); + + this.categories.add(category); + return this; + } + @Override + public YetAnotherConfigLib.Builder categories(@NotNull Collection categories) { + Validate.notNull(categories, "`categories` cannot be null"); + + this.categories.addAll(categories); + return this; + } + + @Override + public YetAnotherConfigLib.Builder save(@NotNull Runnable saveFunction) { + Validate.notNull(saveFunction, "`saveFunction` cannot be null"); + + this.saveFunction = saveFunction; + return this; + } + + @Override + public YetAnotherConfigLib.Builder screenInit(@NotNull Consumer initConsumer) { + Validate.notNull(initConsumer, "`initConsumer` cannot be null"); + + this.initConsumer = initConsumer; + return this; + } + + @Override + 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`"); + Validate.isTrue(!categories.stream().allMatch(category -> category instanceof PlaceholderCategory), "At least one regular category is required to build `YetAnotherConfigLib`"); + + return new YetAnotherConfigLibImpl(title, ImmutableList.copyOf(categories), saveFunction, initConsumer); + } + } } -- cgit