diff options
Diffstat (limited to 'common/src/main/java/dev/isxander/yacl3/api')
36 files changed, 1400 insertions, 0 deletions
| diff --git a/common/src/main/java/dev/isxander/yacl3/api/Binding.java b/common/src/main/java/dev/isxander/yacl3/api/Binding.java new file mode 100644 index 0000000..f41b78b --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/Binding.java @@ -0,0 +1,64 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.impl.GenericBindingImpl; +import dev.isxander.yacl3.mixin.OptionInstanceAccessor; +import net.minecraft.client.OptionInstance; +import org.apache.commons.lang3.Validate; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Controls modifying the bound option. + * Provides the default value, a setter and a getter. + */ +public interface Binding<T> { +    void setValue(T value); + +    T getValue(); + +    T defaultValue(); + +    /** +     * Creates a generic binding. +     * +     * @param def default value of the option, used to reset +     * @param getter should return the current value of the option +     * @param setter should set the option to the supplied value +     */ +    static <T> Binding<T> generic(T def, Supplier<T> getter, Consumer<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"); + +        return new GenericBindingImpl<>(def, getter, setter); +    } + +    /** +     * Creates a {@link Binding} for Minecraft's {@link OptionInstance} +     */ +    static <T> Binding<T> minecraft(OptionInstance<T> minecraftOption) { +        Validate.notNull(minecraftOption, "`minecraftOption` must not be null"); + +        return new GenericBindingImpl<>( +                ((OptionInstanceAccessor<T>) (Object) minecraftOption).getInitialValue(), +                minecraftOption::get, +                minecraftOption::set +        ); +    } + +    /** +     * Creates an immutable binding that has no default and cannot be modified. +     * +     * @param value the value for the binding +     */ +    static <T> Binding<T> immutable(T value) { +        Validate.notNull(value, "`value` must not be null"); + +        return new GenericBindingImpl<>( +                value, +                () -> value, +                changed -> {} +        ); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java b/common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java new file mode 100644 index 0000000..943f9ac --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/ButtonOption.java @@ -0,0 +1,50 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.impl.ButtonOptionImpl; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public interface ButtonOption extends Option<BiConsumer<YACLScreen, ButtonOption>> { +    /** +     * Action to be executed upon button press +     */ +    BiConsumer<YACLScreen, ButtonOption> action(); + +    static Builder createBuilder() { +        return new ButtonOptionImpl.BuilderImpl(); +    } + +    interface Builder { +        /** +         * Sets the name to be used by the option. +         * +         * @see Option#name() +         */ +        Builder name(@NotNull Component name); + +        Builder description(@NotNull OptionDescription description); + +        Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action); + +        /** +         * Action to be executed upon button press +         * +         * @see ButtonOption#action() +         */ +        @Deprecated +        Builder action(@NotNull Consumer<YACLScreen> action); + +        /** +         * Sets if the option can be configured +         * +         * @see Option#available() +         */ +        Builder available(boolean available); + +        ButtonOption build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java b/common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java new file mode 100644 index 0000000..d47b3ce --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/ConfigCategory.java @@ -0,0 +1,94 @@ +package dev.isxander.yacl3.api; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.impl.ConfigCategoryImpl; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Separates {@link Option}s or {@link OptionGroup}s into multiple distinct sections. + * Served to a user as a button in the left column, + * upon pressing, the options list is filled with options contained within this category. + */ +public interface ConfigCategory { +    /** +     * Name of category, displayed as a button on the left column. +     */ +    @NotNull Component name(); + +    /** +     * Gets every {@link OptionGroup} in this category. +     */ +    @NotNull ImmutableList<OptionGroup> groups(); + +    /** +     * Tooltip (or description) of the category. +     * Rendered on hover. +     */ +    @NotNull Component tooltip(); + +    /** +     * Creates a builder to construct a {@link ConfigCategory} +     */ +    static Builder createBuilder() { +        return new ConfigCategoryImpl.BuilderImpl(); +    } + +    interface Builder extends OptionAddable { +        /** +         * Sets name of the category +         * +         * @see ConfigCategory#name() +         */ +        Builder name(@NotNull Component name); + +        /** +         * Adds an option to the root group of the category. +         * To add to another group, use {@link Builder#group(OptionGroup)}. +         * To construct an option, use {@link Option#createBuilder(Class)} +         * +         * @see ConfigCategory#groups() +         * @see OptionGroup#isRoot() +         */ +        @Override +        Builder option(@NotNull Option<?> option); + +        /** +         * Adds multiple options to the root group of the category. +         * To add to another group, use {@link Builder#groups(Collection)}. +         * To construct an option, use {@link Option#createBuilder(Class)} +         * +         * @see ConfigCategory#groups() +         * @see OptionGroup#isRoot() +         */ +        @Override +        Builder options(@NotNull Collection<? extends Option<?>> options); + +        /** +         * Adds an option group. +         * To add an option to the root group, use {@link Builder#option(Option)} +         * To construct a group, use {@link OptionGroup#createBuilder()} +         */ +        Builder group(@NotNull OptionGroup group); + +        /** +         * Adds multiple option groups. +         * To add multiple options to the root group, use {@link Builder#options(Collection)} +         * To construct a group, use {@link OptionGroup#createBuilder()} +         */ +        Builder groups(@NotNull Collection<OptionGroup> groups); + +        /** +         * 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()}. +         */ +        Builder tooltip(@NotNull Component... tooltips); + +        ConfigCategory build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/Controller.java b/common/src/main/java/dev/isxander/yacl3/api/Controller.java new file mode 100644 index 0000000..25e4465 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/Controller.java @@ -0,0 +1,28 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.AbstractWidget; +import dev.isxander.yacl3.gui.YACLScreen; +import net.minecraft.network.chat.Component; + +/** + * Provides a widget to control the option. + */ +public interface Controller<T> { +    /** +     * Gets the dedicated {@link Option} for this controller +     */ +    Option<T> option(); + +    /** +     * Gets the formatted value based on {@link Option#pendingValue()} +     */ +    Component formatValue(); + +    /** +     * Provides a widget to display +     * +     * @param screen parent screen +     */ +    AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/LabelOption.java b/common/src/main/java/dev/isxander/yacl3/api/LabelOption.java new file mode 100644 index 0000000..a5f015e --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/LabelOption.java @@ -0,0 +1,41 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.impl.LabelOptionImpl; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * A label option is an easier way of creating a label with a {@link dev.isxander.yacl3.gui.controllers.LabelController}. + * This option is immutable and cannot be disabled. Tooltips are supported through + * {@link Component} styling. + */ +public interface LabelOption extends Option<Component> { +    @NotNull Component label(); + +    /** +     * Creates a new label option with the given label, skipping a builder for ease. +     */ +    static LabelOption create(@NotNull Component label) { +        return new LabelOptionImpl(label); +    } + +    static Builder createBuilder() { +        return new LabelOptionImpl.BuilderImpl(); +    } + +    interface Builder { +        /** +         * Appends a line to the label +         */ +        Builder line(@NotNull Component line); + +        /** +         * Appends multiple lines to the label +         */ +        Builder lines(@NotNull Collection<? extends Component> lines); + +        LabelOption build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/ListOption.java b/common/src/main/java/dev/isxander/yacl3/api/ListOption.java new file mode 100644 index 0000000..8094ee7 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/ListOption.java @@ -0,0 +1,146 @@ +package dev.isxander.yacl3.api; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.impl.ListOptionImpl; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +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; + +/** + * A list option that takes form as an option group for UX. + * You add this option through {@link ConfigCategory.Builder#group(OptionGroup)}. Do NOT add as an option. + * Users can add remove and reshuffle a list type. You can use any controller you wish, there are no dedicated + * controllers for list types. List options do not manipulate your list but get and set the list with a + * regular binding for simplicity. + * + * You may apply option flags like a normal option and collapse like a normal group, it is a merge of them both. + * Methods in this interface marked with {@link ApiStatus.Internal} should not be used, and could be subject to + * change at any time + * @param <T> + */ +public interface ListOption<T> extends OptionGroup, Option<List<T>> { +    @Override +    @NotNull ImmutableList<ListOptionEntry<T>> options(); + +    @ApiStatus.Internal +    ListOptionEntry<T> insertNewEntryToTop(); + +    @ApiStatus.Internal +    void insertEntry(int index, ListOptionEntry<?> entry); + +    @ApiStatus.Internal +    int indexOf(ListOptionEntry<?> entry); + +    @ApiStatus.Internal +    void removeEntry(ListOptionEntry<?> entry); + +    @ApiStatus.Internal +    void addRefreshListener(Runnable changedListener); + +    static <T> Builder<T> createBuilder() { +        return new ListOptionImpl.BuilderImpl<>(); +    } + +    @Deprecated +    static <T> Builder<T> createBuilder(Class<T> typeClass) { +        return createBuilder(); +    } + +    interface Builder<T> { +        /** +         * Sets name of the list, for UX purposes, a name should always be given, +         * but isn't enforced. +         * +         * @see ListOption#name() +         */ +        Builder<T> name(@NotNull Component name); + +        Builder<T> description(@NotNull OptionDescription description); + +        /** +         * Sets the value that is used when creating new entries +         */ +        Builder<T> initial(@NotNull T initialValue); + +        Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controller); + +        /** +         * Sets the controller for the option. +         * This is how you interact and change the options. +         * +         * @see dev.isxander.yacl3.gui.controllers +         */ +        Builder<T> customController(@NotNull Function<ListOptionEntry<T>, Controller<T>> control); + +        /** +         * Sets the binding for the option. +         * Used for default, getter and setter. +         * +         * @see Binding +         */ +        Builder<T> binding(@NotNull Binding<List<T>> binding); + +        /** +         * Sets the binding for the option. +         * Shorthand of {@link Binding#generic(Object, Supplier, Consumer)} +         * +         * @param def default value of the option, used to reset +         * @param getter should return the current value of the option +         * @param setter should set the option to the supplied value +         * @see Binding +         */ +        Builder<T> binding(@NotNull List<T> def, @NotNull Supplier<@NotNull List<T>> getter, @NotNull Consumer<@NotNull List<T>> setter); + +        /** +         * Sets if the option can be configured +         * +         * @see Option#available() +         */ +        Builder<T> available(boolean available); + +        /** +         * Adds a flag to the option. +         * Upon applying changes, all flags are executed. +         * {@link Option#flags()} +         */ +        Builder<T> flag(@NotNull OptionFlag... flag); + +        /** +         * Adds a flag to the option. +         * Upon applying changes, all flags are executed. +         * {@link Option#flags()} +         */ +        Builder<T> flags(@NotNull Collection<OptionFlag> flags); + +        /** +         * Dictates if the group should be collapsed by default. +         * If not set, it will not be collapsed by default. +         * +         * @see OptionGroup#collapsed() +         */ +        Builder<T> collapsed(boolean collapsible); + +        /** +         * Adds a listener to the option. Invoked upon changing any of the list's entries. +         * +         * @see Option#addListener(BiConsumer) +         */ +        ListOption.Builder<T> listener(@NotNull BiConsumer<Option<List<T>>, List<T>> listener); + +        /** +         * Adds multiple listeners to the option. Invoked upon changing of any of the list's entries. +         * +         * @see Option#addListener(BiConsumer) +         */ +        ListOption.Builder<T> listeners(@NotNull Collection<BiConsumer<Option<List<T>>, List<T>>> listeners); + +        ListOption<T> build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java b/common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java new file mode 100644 index 0000000..23ec657 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/ListOptionEntry.java @@ -0,0 +1,18 @@ +package dev.isxander.yacl3.api; + +import com.google.common.collect.ImmutableSet; +import org.jetbrains.annotations.NotNull; + +public interface ListOptionEntry<T> extends Option<T> { +    ListOption<T> parentGroup(); + +    @Override +    default @NotNull ImmutableSet<OptionFlag> flags() { +        return parentGroup().flags(); +    } + +    @Override +    default boolean available() { +        return parentGroup().available(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java b/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java new file mode 100644 index 0000000..5a50207 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/NameableEnum.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api; + +import net.minecraft.network.chat.Component; + +/** + * Used for the default value formatter of {@link dev.isxander.yacl3.gui.controllers.cycling.EnumController} + */ +public interface NameableEnum { +    Component getDisplayName(); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/Option.java b/common/src/main/java/dev/isxander/yacl3/api/Option.java new file mode 100644 index 0000000..31b7756 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/Option.java @@ -0,0 +1,223 @@ +package dev.isxander.yacl3.api; + +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.impl.OptionImpl; +import net.minecraft.network.chat.Component; +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; + +public interface Option<T> { +    /** +     * Name of the option +     */ +    @NotNull Component name(); + +    @NotNull OptionDescription description(); + +    /** +     * Tooltip (or description) of the option. +     * Rendered on hover. +     */ +    @Deprecated +    @NotNull Component tooltip(); + +    /** +     * Widget provider for a type of option. +     * +     * @see dev.isxander.yacl3.gui.controllers +     */ +    @NotNull Controller<T> controller(); + +    /** +     * Binding for the option. +     * Controls setting, getting and default value. +     * +     * @see Binding +     */ +    @NotNull Binding<T> binding(); + +    /** +     * If the option can be configured +     */ +    boolean available(); + +    /** +     * Sets if the option can be configured after being built +     * +     * @see Option#available() +     */ +    void setAvailable(boolean available); + +    /** +     * Tasks that needs to be executed upon applying changes. +     */ +    @NotNull ImmutableSet<OptionFlag> flags(); + +    /** +     * Checks if the pending value is not equal to the current set value +     */ +    boolean changed(); + +    /** +     * Value in the GUI, ready to set the actual bound value or be undone. +     */ +    @NotNull T pendingValue(); + +    /** +     * Sets the pending value +     */ +    void requestSet(T value); + +    /** +     * Applies the pending value to the bound value. +     * Cannot be undone. +     * +     * @return if there were changes to apply {@link Option#changed()} +     */ +    boolean applyValue(); + +    /** +     * Sets the pending value to the bound value. +     */ +    void forgetPendingValue(); + +    /** +     * Sets the pending value to the default bound value. +     */ +    void requestSetDefault(); + +    /** +     * Checks if the current pending value is equal to its default value +     */ +    boolean isPendingValueDefault(); + +    default boolean canResetToDefault() { +        return true; +    } + +    /** +     * Adds a listener for when the pending value changes +     */ +    void addListener(BiConsumer<Option<T>, T> changedListener); + +    static <T> Builder<T> createBuilder() { +        return new OptionImpl.BuilderImpl<>(); +    } + +    /** +     * Creates a builder to construct an {@link Option} +     * +     * @param <T> type of the option's value +     * @param typeClass used to capture the type +     */ +    @Deprecated +    static <T> Builder<T> createBuilder(Class<T> typeClass) { +        return createBuilder(); +    } + +    interface Builder<T> { +        /** +         * Sets the name to be used by the option. +         * +         * @see Option#name() +         */ +        Builder<T> name(@NotNull Component name); + +        /** +         * Sets the description to be used by the option. +         * @see OptionDescription +         * @param description the static description. +         * @return this builder +         */ +        Builder<T> description(@NotNull OptionDescription description); + +        /** +         * Sets the function to get the description by the option's current value. +         * +         * @see OptionDescription +         * @param descriptionFunction the function to get the description by the option's current value. +         * @return this builder +         */ +        Builder<T> description(@NotNull Function<T, OptionDescription> descriptionFunction); + +        Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controllerBuilder); + +        /** +         * Sets the controller for the option. +         * This is how you interact and change the options. +         * +         * @see dev.isxander.yacl3.gui.controllers +         */ +        Builder<T> customController(@NotNull Function<Option<T>, Controller<T>> control); + +        /** +         * Sets the binding for the option. +         * Used for default, getter and setter. +         * +         * @see Binding +         */ +        Builder<T> binding(@NotNull Binding<T> binding); + +        /** +         * Sets the binding for the option. +         * Shorthand of {@link Binding#generic(Object, Supplier, Consumer)} +         * +         * @param def default value of the option, used to reset +         * @param getter should return the current value of the option +         * @param setter should set the option to the supplied value +         * @see Binding +         */ +        Builder<T> binding(@NotNull T def, @NotNull Supplier<@NotNull T> getter, @NotNull Consumer<@NotNull T> setter); + +        /** +         * Sets if the option can be configured +         * +         * @see Option#available() +         */ +        Builder<T> available(boolean available); + +        /** +         * Adds a flag to the option. +         * Upon applying changes, all flags are executed. +         * {@link Option#flags()} +         */ +        Builder<T> flag(@NotNull OptionFlag... flag); + +        /** +         * Adds a flag to the option. +         * Upon applying changes, all flags are executed. +         * {@link Option#flags()} +         */ +        Builder<T> flags(@NotNull Collection<? extends OptionFlag> flags); + +        /** +         * Instantly invokes the binder's setter when modified in the GUI. +         * Prevents the user from undoing the change +         * <p> +         * Does not support {@link Option#flags()}! +         */ +        Builder<T> instant(boolean instant); + +        /** +         * Adds a listener to the option. Invoked upon changing the pending value. +         * +         * @see Option#addListener(BiConsumer) +         */ +        Builder<T> listener(@NotNull BiConsumer<Option<T>, T> listener); + +        /** +         * Adds multiple listeners to the option. Invoked upon changing the pending value. +         * +         * @see Option#addListener(BiConsumer) +         */ +        Builder<T> listeners(@NotNull Collection<BiConsumer<Option<T>, T>> listeners); + +        Option<T> build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java b/common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java new file mode 100644 index 0000000..97ab352 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/OptionAddable.java @@ -0,0 +1,19 @@ +package dev.isxander.yacl3.api; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public interface OptionAddable { +    /** +     * Adds an option to an abstract builder. +     * To construct an option, use {@link Option#createBuilder(Class)} +     */ +    OptionAddable option(@NotNull Option<?> option); + +    /** +     * Adds multiple options to an abstract builder. +     * To construct an option, use {@link Option#createBuilder(Class)} +     */ +    OptionAddable options(@NotNull Collection<? extends Option<?>> options); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java b/common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java new file mode 100644 index 0000000..40f1d68 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/OptionDescription.java @@ -0,0 +1,161 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.gui.ImageRenderer; +import dev.isxander.yacl3.impl.OptionDescriptionImpl; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * Provides all information for the description panel in the GUI. + * This provides no functional benefit, and is purely for UX. + */ +public interface OptionDescription { +    /** +     * The description of the option, this is automatically wrapped and supports all styling, +     * including {@link net.minecraft.network.chat.ClickEvent}s and {@link net.minecraft.network.chat.HoverEvent}s. +     * @return The description of the option, with all lines merged with \n. +     */ +    Component text(); + +    /** +     * The image to display with the description. If the Optional is empty, no image has been provided. +     * Usually, the image renderers are constructed asynchronously, so this method returns a {@link CompletableFuture}. +     * <p> +     * Image renderers are cached throughout the whole lifecycle of the game, and should not be generated more than once +     * per image. See {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)} for implementation details. +     */ +    CompletableFuture<Optional<ImageRenderer>> image(); + +    /** +     * @return a new builder for an {@link OptionDescription}. +     */ +    static Builder createBuilder() { +        return new OptionDescriptionImpl.BuilderImpl(); +    } + +    static OptionDescription of(Component... description) { +        return createBuilder().text(description).build(); +    } + +    OptionDescription EMPTY = new OptionDescriptionImpl(CommonComponents.EMPTY, CompletableFuture.completedFuture(Optional.empty())); + +    interface Builder { +        /** +         * Appends lines to the main description of the option. This can be called multiple times. +         * On {@link Builder#build()}, the lines are merged with \n. +         * @see OptionDescription#text() +         * +         * @param description the lines to append to the description. +         * @return this builder +         */ +        Builder text(Component... description); + +        /** +         * Appends lines to the main description of the option. This can be called multiple times. +         * On {@link Builder#build()}, the lines are merged with \n. +         * @see OptionDescription#text() +         * +         * @param lines the lines to append to the description. +         * @return this builder +         */ +        Builder text(Collection<? extends Component> lines); + +        /** +         * Sets a static image to display with the description. This is backed by a regular minecraft resource +         * in your mod's /assets folder. +         * +         * @param image the location of the image to display from the resource manager +         * @param width the width of the texture +         * @param height the height of the texture +         * @return this builder +         */ +        Builder image(ResourceLocation image, int width, int height); + +        /** +         * Sets a static image to display with the description. This is backed by a regular minecraft resource +         * in your mod's /assets folder. This overload method allows you to specify a subsection of the texture to render. +         * +         * @param image the location of the image to display from the resource manager +         * @param u the u coordinate +         * @param v the v coordinate +         * @param width the width of the subsection +         * @param height the height of the subsection +         * @param textureWidth the width of the whole texture file +         * @param textureHeight the height of whole texture file +         * @return this builder +         */ +        Builder image(ResourceLocation image, float u, float v, int width, int height, int textureWidth, int textureHeight); + +        /** +         * Sets a static image to display with the description. This is backed by a file on disk. +         * The width and height is automatically determined from the image processing. +         * +         * @param path the absolute path to the image file +         * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar +         * @return this builder +         */ +        Builder image(Path path, ResourceLocation uniqueLocation); + +        /** +         * Sets a static OR ANIMATED webP image to display with the description. This is backed by a regular minecraft resource +         * in your mod's /assets folder. +         * +         * @param image the location of the image to display from the resource manager +         * @return this builder +         */ +        Builder webpImage(ResourceLocation image); + +        /** +         * Sets a static OR ANIMATED webP image to display with the description. This is backed by a file on disk. +         * The width and height is automatically determined from the image processing. +         * +         * @param path the absolute path to the image file +         * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar +         * @return this builder +         */ +        Builder webpImage(Path path, ResourceLocation uniqueLocation); + +        /** +         * Sets a custom image renderer to display with the description. +         * This is useful for rendering other abstract things relevant to your mod. +         * <p> +         * However, <strong>THIS IS NOT API SAFE!</strong> As part of the gui package, things +         * may change that could break compatibility with future versions of YACL. +         * A helpful utility (that is also not API safe) is {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)} +         * which will cache the image renderer for the whole game lifecycle and construct it asynchronously to the render thread. +         * @param image the image renderer to display +         * @return this builder +         */ +        Builder customImage(CompletableFuture<Optional<ImageRenderer>> image); + +        /** +         * Sets an animated GIF image to display with the description. This is backed by a regular minecraft resource +         * in your mod's /assets folder. +         * +         * @param image the location of the image to display from the resource manager +         * @return this builder +         */ +        @Deprecated +        Builder gifImage(ResourceLocation image); + +        /** +         * Sets an animated GIF image to display with the description. This is backed by a file on disk. +         * The width and height is automatically determined from the image processing. +         * +         * @param path the absolute path to the image file +         * @param uniqueLocation the unique identifier for the image, used for caching and resource manager registrar +         * @return this builder +         */ +        @Deprecated +        Builder gifImage(Path path, ResourceLocation uniqueLocation); + +        OptionDescription build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java b/common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java new file mode 100644 index 0000000..6f35495 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/OptionFlag.java @@ -0,0 +1,23 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.gui.RequireRestartScreen; +import net.minecraft.client.Minecraft; + +import java.util.function.Consumer; + +/** + * Code that is executed upon certain options being applied. + * Each flag is executed only once per save, no matter the amount of options with the flag. + */ +@FunctionalInterface +public interface OptionFlag extends Consumer<Minecraft> { +    /** Warns the user that a game restart is required for the changes to take effect */ +    OptionFlag GAME_RESTART = client -> client.setScreen(new RequireRestartScreen(client.screen)); + +    /** Reloads chunks upon applying (F3+A) */ +    OptionFlag RELOAD_CHUNKS = client -> client.levelRenderer.allChanged(); + +    OptionFlag WORLD_RENDER_UPDATE = client -> client.levelRenderer.needsUpdate(); + +    OptionFlag ASSET_RELOAD = Minecraft::delayTextureReload; +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java b/common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java new file mode 100644 index 0000000..ff31966 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/OptionGroup.java @@ -0,0 +1,90 @@ +package dev.isxander.yacl3.api; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.impl.OptionGroupImpl; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Serves as a separator between multiple chunks of options + * that may be too similar or too few to be placed in a separate {@link ConfigCategory}. + * Or maybe you just want your config to feel less dense. + */ +public interface OptionGroup { +    /** +     * Name of the option group, displayed as a separator in the option lists. +     * Can be empty. +     */ +    Component name(); + +    OptionDescription description(); + +    /** +     * Tooltip displayed on hover. +     */ +    @Deprecated +    Component tooltip(); + +    /** +     * List of all options in the group +     */ +    @NotNull ImmutableList<? extends Option<?>> options(); + +    /** +     * Dictates if the group should be collapsed by default. +     */ +    boolean collapsed(); + +    /** +     * Always false when using the {@link Builder} +     * used to not render the separator if true +     */ +    boolean isRoot(); + +    /** +     * Creates a builder to construct a {@link OptionGroup} +     */ +    static Builder createBuilder() { +        return new OptionGroupImpl.BuilderImpl(); +    } + +    interface Builder extends OptionAddable { +        /** +         * Sets name of the group, can be {@link Component#empty()} to just separate options, like sodium. +         * +         * @see OptionGroup#name() +         */ +        Builder name(@NotNull Component name); + +        Builder description(@NotNull OptionDescription description); + +        /** +         * Adds an option to group. +         * To construct an option, use {@link Option#createBuilder(Class)} +         * +         * @see OptionGroup#options() +         */ +        @Override +        Builder option(@NotNull Option<?> option); + +        /** +         * Adds multiple options to group. +         * To construct an option, use {@link Option#createBuilder(Class)} +         * +         * @see OptionGroup#options() +         */ +        @Override +        Builder options(@NotNull Collection<? extends Option<?>> options); + +        /** +         * Dictates if the group should be collapsed by default +         * +         * @see OptionGroup#collapsed() +         */ +        Builder collapsed(boolean collapsible); + +        OptionGroup build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java b/common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java new file mode 100644 index 0000000..6eb82c5 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/PlaceholderCategory.java @@ -0,0 +1,55 @@ +package dev.isxander.yacl3.api; + +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.impl.PlaceholderCategoryImpl; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiFunction; + +/** + * A placeholder category that actually just opens another screen, + * instead of displaying options. + * <p> + * Use of this is discouraged, as it is not very user-friendly and navigating to a placeholder + * tab that opens another screen is not very intuitive, making keyboard navigation impossible. + */ +public interface PlaceholderCategory extends ConfigCategory { +    /** +     * Function to create a screen to open upon changing to this category +     */ +    BiFunction<Minecraft, YACLScreen, Screen> screen(); + +    static Builder createBuilder() { +        return new PlaceholderCategoryImpl.BuilderImpl(); +    } + +    interface Builder { +        /** +         * Sets name of the category +         * +         * @see ConfigCategory#name() +         */ +        Builder name(@NotNull Component name); + +        /** +         * Sets the tooltip to be used by the category. +         * Can be invoked twice to append more lines. +         * No need to wrap the Component yourself, the gui does this itself. +         * +         * @param tooltips Component lines - merged with a new-line on {@link dev.isxander.yacl3.api.PlaceholderCategory.Builder#build()}. +         */ +        Builder tooltip(@NotNull Component... tooltips); + +        /** +         * Screen to open upon selecting this category +         * +         * @see PlaceholderCategory#screen() +         */ +        Builder screen(@NotNull BiFunction<Minecraft, YACLScreen, Screen> screenFunction); + +        PlaceholderCategory build(); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java b/common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java new file mode 100644 index 0000000..15ce5bc --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/YetAnotherConfigLib.java @@ -0,0 +1,107 @@ +package dev.isxander.yacl3.api; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.config.ConfigInstance; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.impl.YetAnotherConfigLibImpl; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.function.Consumer; + +/** + * Main class of the mod. + * Contains all data and used to provide a {@link Screen} + */ +public interface YetAnotherConfigLib { +    /** +     * Title of the GUI. Only used for Minecraft narration. +     */ +    Component title(); + +    /** +     * Gets all config categories. +     */ +    ImmutableList<ConfigCategory> categories(); + +    /** +     * Ran when changes are saved. Can be used to save config to a file etc. +     */ +    Runnable saveFunction(); + +    /** +     * Ran every time the YACL screen initialises. Can be paired with FAPI to add custom widgets. +     */ +    Consumer<YACLScreen> initConsumer(); + +    /** +     * Generates a Screen to display based on this instance. +     * +     * @param parent parent screen to open once closed +     */ +    Screen generateScreen(@Nullable Screen parent); + +    /** +     * Creates a builder to construct YACL +     */ +    static Builder createBuilder() { +        return new YetAnotherConfigLibImpl.BuilderImpl(); +    } + +    /** +     * Creates an instance using a {@link ConfigInstance} which autofills the save() builder method. +     * This also takes an easy functional interface that provides defaults and config to help build YACL bindings. +     */ +    static <T> YetAnotherConfigLib create(ConfigInstance<T> configInstance, ConfigBackedBuilder<T> builder) { +        return builder.build(configInstance.getDefaults(), configInstance.getConfig(), createBuilder().save(configInstance::save)).build(); +    } + +    interface Builder { +        /** +         * Sets title of GUI for Minecraft narration +         * +         * @see YetAnotherConfigLib#title() +         */ +        Builder title(@NotNull Component title); + +        /** +         * Adds a new category. +         * To create a category you need to use {@link ConfigCategory#createBuilder()} +         * +         * @see YetAnotherConfigLib#categories() +         */ +        Builder category(@NotNull ConfigCategory category); + +        /** +         * Adds multiple categories at once. +         * To create a category you need to use {@link ConfigCategory#createBuilder()} +         * +         * @see YetAnotherConfigLib#categories() +         */ +        Builder categories(@NotNull Collection<? extends ConfigCategory> categories); + +        /** +         * Used to define a save function for when user clicks the Save Changes button +         * +         * @see YetAnotherConfigLib#saveFunction() +         */ +        Builder save(@NotNull Runnable saveFunction); + +        /** +         * Defines a consumer that is accepted every time the YACL screen initialises +         * +         * @see YetAnotherConfigLib#initConsumer() +         */ +        Builder screenInit(@NotNull Consumer<YACLScreen> initConsumer); + +        YetAnotherConfigLib build(); +    } + +    @FunctionalInterface +    interface ConfigBackedBuilder<T> { +        YetAnotherConfigLib.Builder build(T defaults, T config, YetAnotherConfigLib.Builder builder); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java new file mode 100644 index 0000000..88f9a77 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/BooleanControllerBuilder.java @@ -0,0 +1,16 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.BooleanControllerBuilderImpl; + +public interface BooleanControllerBuilder extends ValueFormattableController<Boolean, BooleanControllerBuilder> { +    BooleanControllerBuilder coloured(boolean coloured); + +    BooleanControllerBuilder onOffFormatter(); +    BooleanControllerBuilder yesNoFormatter(); +    BooleanControllerBuilder trueFalseFormatter(); + +    static BooleanControllerBuilder create(Option<Boolean> option) { +        return new BooleanControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java new file mode 100644 index 0000000..8e442ff --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/ColorControllerBuilder.java @@ -0,0 +1,14 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.ColorControllerBuilderImpl; + +import java.awt.Color; + +public interface ColorControllerBuilder extends ControllerBuilder<Color> { +    ColorControllerBuilder allowAlpha(boolean allowAlpha); + +    static ColorControllerBuilder create(Option<Color> option) { +        return new ColorControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java new file mode 100644 index 0000000..0cd3a55 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/ControllerBuilder.java @@ -0,0 +1,9 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Controller; +import org.jetbrains.annotations.ApiStatus; + +public interface ControllerBuilder<T> { +    @ApiStatus.Internal +    Controller<T> build(); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java new file mode 100644 index 0000000..8c9ea91 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/CyclingListControllerBuilder.java @@ -0,0 +1,15 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.CyclingListControllerBuilderImpl; + +public interface CyclingListControllerBuilder<T> extends ValueFormattableController<T, CyclingListControllerBuilder<T>> { +    @SuppressWarnings("unchecked") +    CyclingListControllerBuilder<T> values(T... values); + +    CyclingListControllerBuilder<T> values(Iterable<? extends T> values); + +    static <T> CyclingListControllerBuilder<T> create(Option<T> option) { +        return new CyclingListControllerBuilderImpl<>(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java new file mode 100644 index 0000000..db4af94 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleFieldControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.DoubleFieldControllerBuilderImpl; + +public interface DoubleFieldControllerBuilder extends NumberFieldControllerBuilder<Double, DoubleFieldControllerBuilder> { +    static DoubleFieldControllerBuilder create(Option<Double> option) { +        return new DoubleFieldControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java new file mode 100644 index 0000000..7e4b6f9 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/DoubleSliderControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.DoubleSliderControllerBuilderImpl; + +public interface DoubleSliderControllerBuilder extends SliderControllerBuilder<Double, DoubleSliderControllerBuilder> { +    static DoubleSliderControllerBuilder create(Option<Double> option) { +        return new DoubleSliderControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java new file mode 100644 index 0000000..decb8f9 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/EnumControllerBuilder.java @@ -0,0 +1,12 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.EnumControllerBuilderImpl; + +public interface EnumControllerBuilder<T extends Enum<T>> extends ValueFormattableController<T, EnumControllerBuilder<T>> { +    EnumControllerBuilder<T> enumClass(Class<T> enumClass); + +    static <T extends Enum<T>> EnumControllerBuilder<T> create(Option<T> option) { +        return new EnumControllerBuilderImpl<>(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java new file mode 100644 index 0000000..de81837 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatFieldControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.FloatFieldControllerBuilderImpl; + +public interface FloatFieldControllerBuilder extends NumberFieldControllerBuilder<Float, FloatFieldControllerBuilder> { +    static FloatFieldControllerBuilder create(Option<Float> option) { +        return new FloatFieldControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java new file mode 100644 index 0000000..2a04dde --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/FloatSliderControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.FloatSliderControllerBuilderImpl; + +public interface FloatSliderControllerBuilder extends SliderControllerBuilder<Float, FloatSliderControllerBuilder> { +    static FloatSliderControllerBuilder create(Option<Float> option) { +        return new FloatSliderControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java new file mode 100644 index 0000000..1e31fac --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerFieldControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.IntegerFieldControllerBuilderImpl; + +public interface IntegerFieldControllerBuilder extends NumberFieldControllerBuilder<Integer, IntegerFieldControllerBuilder> { +    static IntegerFieldControllerBuilder create(Option<Integer> option) { +        return new IntegerFieldControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java new file mode 100644 index 0000000..11e089a --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/IntegerSliderControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.IntegerSliderControllerBuilderImpl; + +public interface IntegerSliderControllerBuilder extends SliderControllerBuilder<Integer, IntegerSliderControllerBuilder> { +    static IntegerSliderControllerBuilder create(Option<Integer> option) { +        return new IntegerSliderControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java new file mode 100644 index 0000000..c53b464 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/LongFieldControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.LongFieldControllerBuilderImpl; + +public interface LongFieldControllerBuilder extends NumberFieldControllerBuilder<Long, LongFieldControllerBuilder> { +    static LongFieldControllerBuilder create(Option<Long> option) { +        return new LongFieldControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java new file mode 100644 index 0000000..fc09423 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/LongSliderControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.LongSliderControllerBuilderImpl; + +public interface LongSliderControllerBuilder extends SliderControllerBuilder<Long, LongSliderControllerBuilder> { +    static LongSliderControllerBuilder create(Option<Long> option) { +        return new LongSliderControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java new file mode 100644 index 0000000..b5cfa1f --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/NumberFieldControllerBuilder.java @@ -0,0 +1,7 @@ +package dev.isxander.yacl3.api.controller; + +public interface NumberFieldControllerBuilder<T extends Number, B extends NumberFieldControllerBuilder<T, B>> extends ValueFormattableController<T, B> { +    B min(T min); +    B max(T max); +    B range(T min, T max); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java new file mode 100644 index 0000000..2fb3fec --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/SliderControllerBuilder.java @@ -0,0 +1,6 @@ +package dev.isxander.yacl3.api.controller; + +public interface SliderControllerBuilder<T extends Number, B extends SliderControllerBuilder<T, B>> extends ValueFormattableController<T, B> { +    B range(T min, T max); +    B step(T step); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java new file mode 100644 index 0000000..5e2f8c6 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/StringControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.StringControllerBuilderImpl; + +public interface StringControllerBuilder extends ControllerBuilder<String> { +    static StringControllerBuilder create(Option<String> option) { +        return new StringControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java b/common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java new file mode 100644 index 0000000..71a2762 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/TickBoxControllerBuilder.java @@ -0,0 +1,10 @@ +package dev.isxander.yacl3.api.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.TickBoxControllerBuilderImpl; + +public interface TickBoxControllerBuilder extends ControllerBuilder<Boolean> { +    static TickBoxControllerBuilder create(Option<Boolean> option) { +        return new TickBoxControllerBuilderImpl(option); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java b/common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java new file mode 100644 index 0000000..af55e55 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/controller/ValueFormattableController.java @@ -0,0 +1,9 @@ +package dev.isxander.yacl3.api.controller; + +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public interface ValueFormattableController<T, B extends ValueFormattableController<T, B>> extends ControllerBuilder<T> { +    B valueFormatter(Function<T, Component> formatter); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java b/common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java new file mode 100644 index 0000000..ec09238 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/utils/Dimension.java @@ -0,0 +1,33 @@ +package dev.isxander.yacl3.api.utils; + +import dev.isxander.yacl3.impl.utils.DimensionIntegerImpl; + +public interface Dimension<T extends Number> { +    T x(); +    T y(); + +    T width(); +    T height(); + +    T xLimit(); +    T yLimit(); + +    T centerX(); +    T centerY(); + +    boolean isPointInside(T x, T y); + +    MutableDimension<T> clone(); + +    Dimension<T> withX(T x); +    Dimension<T> withY(T y); +    Dimension<T> withWidth(T width); +    Dimension<T> withHeight(T height); + +    Dimension<T> moved(T x, T y); +    Dimension<T> expanded(T width, T height); + +    static MutableDimension<Integer> ofInt(int x, int y, int width, int height) { +        return new DimensionIntegerImpl(x, y, width, height); +    } +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java b/common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java new file mode 100644 index 0000000..f551232 --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/utils/MutableDimension.java @@ -0,0 +1,11 @@ +package dev.isxander.yacl3.api.utils; + +public interface MutableDimension<T extends Number> extends Dimension<T> { +    MutableDimension<T> setX(T x); +    MutableDimension<T> setY(T y); +    MutableDimension<T> setWidth(T width); +    MutableDimension<T> setHeight(T height); + +    MutableDimension<T> move(T x, T y); +    MutableDimension<T> expand(T width, T height); +} diff --git a/common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java b/common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java new file mode 100644 index 0000000..cf33f0f --- /dev/null +++ b/common/src/main/java/dev/isxander/yacl3/api/utils/OptionUtils.java @@ -0,0 +1,39 @@ +package dev.isxander.yacl3.api.utils; + +import dev.isxander.yacl3.api.*; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class OptionUtils { +    /** +     * Consumes all options, ignoring groups and categories. +     * When consumer returns true, this function stops iterating. +     */ +    public static void consumeOptions(YetAnotherConfigLib yacl, Function<Option<?>, Boolean> consumer) { +        for (ConfigCategory category : yacl.categories()) { +            for (OptionGroup group : category.groups()) { +                if (group instanceof ListOption<?> list) { +                    if (consumer.apply(list)) return; +                } else { +                    for (Option<?> option : group.options()) { +                        if (consumer.apply(option)) return; +                    } +                } + +            } +        } +    } + +    /** +     * Consumes all options, ignoring groups and categories. +     * +     * @see OptionUtils#consumeOptions(YetAnotherConfigLib, Function) +     */ +    public static void forEachOptions(YetAnotherConfigLib yacl, Consumer<Option<?>> consumer) { +        consumeOptions(yacl, (opt) -> { +            consumer.accept(opt); +            return false; +        }); +    } +} | 
