diff options
Diffstat (limited to 'src/main/java/dev/isxander/yacl3/impl')
33 files changed, 2808 insertions, 0 deletions
diff --git a/src/main/java/dev/isxander/yacl3/impl/ButtonOptionImpl.java b/src/main/java/dev/isxander/yacl3/impl/ButtonOptionImpl.java new file mode 100644 index 0000000..170b8e0 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/ButtonOptionImpl.java @@ -0,0 +1,205 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.ActionController; +import net.minecraft.network.chat.Component; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +@ApiStatus.Internal +public final class ButtonOptionImpl implements ButtonOption { + private final Component name; + private final OptionDescription description; + private final BiConsumer<YACLScreen, ButtonOption> action; + private boolean available; + private final Controller<BiConsumer<YACLScreen, ButtonOption>> controller; + private final Binding<BiConsumer<YACLScreen, ButtonOption>> binding; + + public ButtonOptionImpl( + @NotNull Component name, + @Nullable OptionDescription description, + @NotNull BiConsumer<YACLScreen, ButtonOption> action, + @Nullable Component text, + boolean available + ) { + this.name = name; + this.description = description; + this.action = action; + this.available = available; + this.controller = text != null ? new ActionController(this, text) : new ActionController(this); + this.binding = new EmptyBinderImpl(); + } + + @Override + public @NotNull Component name() { + return name; + } + + @Override + public @NotNull OptionDescription description() { + return description; + } + + @Override + public @NotNull Component tooltip() { + return description().text(); + } + + @Override + public BiConsumer<YACLScreen, ButtonOption> action() { + return action; + } + + @Override + public boolean available() { + return available; + } + + @Override + public void setAvailable(boolean available) { + this.available = available; + } + + @Override + public @NotNull Controller<BiConsumer<YACLScreen, ButtonOption>> controller() { + return controller; + } + + @Override + public @NotNull Binding<BiConsumer<YACLScreen, ButtonOption>> binding() { + return binding; + } + + @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return ImmutableSet.of(); + } + + @Override + public boolean changed() { + return false; + } + + @Override + public @NotNull BiConsumer<YACLScreen, ButtonOption> pendingValue() { + throw new UnsupportedOperationException(); + } + + @Override + public void requestSet(@NotNull BiConsumer<YACLScreen, ButtonOption> value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean applyValue() { + return false; + } + + @Override + public void forgetPendingValue() { + + } + + @Override + public void requestSetDefault() { + + } + + @Override + public boolean isPendingValueDefault() { + throw new UnsupportedOperationException(); + } + + @Override + public void addListener(BiConsumer<Option<BiConsumer<YACLScreen, ButtonOption>>, BiConsumer<YACLScreen, ButtonOption>> changedListener) { + + } + + private static class EmptyBinderImpl implements Binding<BiConsumer<YACLScreen, ButtonOption>> { + @Override + public void setValue(BiConsumer<YACLScreen, ButtonOption> value) { + + } + + @Override + public BiConsumer<YACLScreen, ButtonOption> getValue() { + throw new UnsupportedOperationException(); + } + + @Override + public BiConsumer<YACLScreen, ButtonOption> defaultValue() { + throw new UnsupportedOperationException(); + } + } + + @ApiStatus.Internal + public static final class BuilderImpl implements Builder { + private Component name; + private Component text = null; + private OptionDescription description = OptionDescription.EMPTY; + private boolean available = true; + private BiConsumer<YACLScreen, ButtonOption> action; + + @Override + public Builder name(@NotNull Component name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public Builder text(@NotNull Component text) { + Validate.notNull(text, "`text` cannot be null"); + + this.text = text; + return this; + } + + @Override + public Builder description(@NotNull OptionDescription description) { + Validate.notNull(description, "`description` cannot be null"); + + this.description = description; + return this; + } + + @Override + public Builder action(@NotNull BiConsumer<YACLScreen, ButtonOption> action) { + Validate.notNull(action, "`action` cannot be null"); + + this.action = action; + return this; + } + + @Override + @Deprecated + public Builder action(@NotNull Consumer<YACLScreen> action) { + Validate.notNull(action, "`action` cannot be null"); + + this.action = (screen, button) -> action.accept(screen); + return this; + } + + @Override + public Builder available(boolean available) { + this.available = available; + return this; + } + + @Override + public ButtonOption build() { + Validate.notNull(name, "`name` must not be null when building `ButtonOption`"); + Validate.notNull(action, "`action` must not be null when building `ButtonOption`"); + + return new ButtonOptionImpl(name, description, action, text, available); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/ConfigCategoryImpl.java b/src/main/java/dev/isxander/yacl3/impl/ConfigCategoryImpl.java new file mode 100644 index 0000000..400abf6 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/ConfigCategoryImpl.java @@ -0,0 +1,134 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.impl.utils.YACLConstants; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +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 final class ConfigCategoryImpl implements ConfigCategory { + private final Component name; + private final ImmutableList<OptionGroup> groups; + private final Component tooltip; + + public ConfigCategoryImpl(Component name, ImmutableList<OptionGroup> groups, Component tooltip) { + this.name = name; + this.groups = groups; + this.tooltip = tooltip; + } + + @Override + public @NotNull Component name() { + return name; + } + + @Override + public @NotNull ImmutableList<OptionGroup> groups() { + return groups; + } + + @Override + public @NotNull Component tooltip() { + return tooltip; + } + + @ApiStatus.Internal + public static final class BuilderImpl implements Builder { + private Component name; + + private final List<Option<?>> rootOptions = new ArrayList<>(); + private final List<OptionGroup> groups = new ArrayList<>(); + + private final List<Component> tooltipLines = new ArrayList<>(); + + @Override + public Builder name(@NotNull Component name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public 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 Builder options(@NotNull Collection<? extends Option<?>> 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 Builder group(@NotNull OptionGroup group) { + Validate.notNull(group, "`group` must not be null"); + + this.groups.add(group); + return this; + } + + @Override + public Builder groups(@NotNull Collection<OptionGroup> groups) { + Validate.notEmpty(groups, "`groups` must not be empty"); + + this.groups.addAll(groups); + return this; + } + + @Override + public Builder tooltip(@NotNull Component... 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<OptionGroup> combinedGroups = new ArrayList<>(); + combinedGroups.add(new OptionGroupImpl(CommonComponents.EMPTY, OptionDescription.EMPTY, ImmutableList.copyOf(rootOptions), false, true)); + combinedGroups.addAll(groups); + + Validate.notEmpty(combinedGroups, "at least one option must be added to build `ConfigCategory`"); + + MutableComponent concatenatedTooltip = Component.empty(); + boolean first = true; + for (Component line : tooltipLines) { + if (line.getContents() == CommonComponents.EMPTY.getContents()) + continue; + + if (!first) concatenatedTooltip.append("\n"); + first = false; + + concatenatedTooltip.append(line); + } + + return new ConfigCategoryImpl(name, ImmutableList.copyOf(combinedGroups), concatenatedTooltip); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/GenericBindingImpl.java b/src/main/java/dev/isxander/yacl3/impl/GenericBindingImpl.java new file mode 100644 index 0000000..972c891 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/GenericBindingImpl.java @@ -0,0 +1,35 @@ +package dev.isxander.yacl3.impl; + +import dev.isxander.yacl3.api.Binding; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public final class GenericBindingImpl<T> implements Binding<T> { + private final T def; + private final Supplier<T> getter; + private final Consumer<T> setter; + + public GenericBindingImpl(T def, Supplier<T> getter, Consumer<T> setting) { + this.def = def; + this.getter = getter; + this.setter = setting; + } + + + @Override + public void setValue(T value) { + setter.accept(value); + } + + @Override + public T getValue() { + return getter.get(); + } + + @Override + public T defaultValue() { + return def; + } + +} diff --git a/src/main/java/dev/isxander/yacl3/impl/HiddenNameListOptionEntry.java b/src/main/java/dev/isxander/yacl3/impl/HiddenNameListOptionEntry.java new file mode 100644 index 0000000..64588f2 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/HiddenNameListOptionEntry.java @@ -0,0 +1,109 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl3.api.*; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; + +public class HiddenNameListOptionEntry<T> implements ListOptionEntry<T> { + private final ListOptionEntry<T> option; + + public HiddenNameListOptionEntry(ListOptionEntry<T> option) { + this.option = option; + } + + @Override + public @NotNull Component name() { + return Component.empty(); + } + + @Override + public @NotNull OptionDescription description() { + return option.description(); + } + + @Override + @Deprecated + public @NotNull Component tooltip() { + return option.tooltip(); + } + + @Override + public @NotNull Controller<T> controller() { + return option.controller(); + } + + @Override + public @NotNull Binding<T> binding() { + return option.binding(); + } + + @Override + public boolean available() { + return option.available(); + } + + @Override + public void setAvailable(boolean available) { + option.setAvailable(available); + } + + @Override + public ListOption<T> parentGroup() { + return option.parentGroup(); + } + + @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return option.flags(); + } + + @Override + public boolean changed() { + return option.changed(); + } + + @Override + public @NotNull T pendingValue() { + return option.pendingValue(); + } + + @Override + public void requestSet(@NotNull T value) { + option.requestSet(value); + } + + @Override + public boolean applyValue() { + return option.applyValue(); + } + + @Override + public void forgetPendingValue() { + option.forgetPendingValue(); + } + + @Override + public void requestSetDefault() { + option.requestSetDefault(); + } + + @Override + public boolean isPendingValueDefault() { + return option.isPendingValueDefault(); + } + + @Override + public boolean canResetToDefault() { + return option.canResetToDefault(); + } + + @Override + public void addListener(BiConsumer<Option<T>, T> changedListener) { + option.addListener(changedListener); + } + + +} diff --git a/src/main/java/dev/isxander/yacl3/impl/LabelOptionImpl.java b/src/main/java/dev/isxander/yacl3/impl/LabelOptionImpl.java new file mode 100644 index 0000000..2bd2e10 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/LabelOptionImpl.java @@ -0,0 +1,160 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.gui.controllers.LabelController; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +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.Iterator; +import java.util.List; +import java.util.function.BiConsumer; + +@ApiStatus.Internal +public final class LabelOptionImpl implements LabelOption { + private final Component label; + private final Component name = Component.literal("Label Option"); + private final OptionDescription description; + private final Component tooltip = Component.empty(); + private final LabelController labelController; + private final Binding<Component> binding; + + public LabelOptionImpl(Component label) { + Validate.notNull(label, "`label` must not be null"); + + this.label = label; + this.labelController = new LabelController(this); + this.binding = Binding.immutable(label); + this.description = OptionDescription.createBuilder() + .text(this.label) + .build(); + } + + @Override + public @NotNull Component label() { + return label; + } + + @Override + public @NotNull Component name() { + return name; + } + + @Override + public @NotNull OptionDescription description() { + return description; + } + + @Override + public @NotNull Component tooltip() { + return tooltip; + } + + @Override + public @NotNull Controller<Component> controller() { + return labelController; + } + + @Override + public @NotNull Binding<Component> binding() { + return binding; + } + + @Override + public boolean available() { + return true; + } + + @Override + public void setAvailable(boolean available) { + throw new UnsupportedOperationException("Label options cannot be disabled."); + } + + @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return ImmutableSet.of(); + } + + @Override + public boolean changed() { + return false; + } + + @Override + public @NotNull Component pendingValue() { + return label; + } + + @Override + public void requestSet(@NotNull Component value) { + + } + + @Override + public boolean applyValue() { + return false; + } + + @Override + public void forgetPendingValue() { + + } + + @Override + public void requestSetDefault() { + + } + + @Override + public boolean isPendingValueDefault() { + return true; + } + + @Override + public boolean canResetToDefault() { + return false; + } + + @Override + public void addListener(BiConsumer<Option<Component>, Component> changedListener) { + + } + + @ApiStatus.Internal + public static final class BuilderImpl implements Builder { + private final List<Component> lines = new ArrayList<>(); + + @Override + public Builder line(@NotNull Component line) { + Validate.notNull(line, "`line` must not be null"); + + this.lines.add(line); + return this; + } + + @Override + public Builder lines(@NotNull Collection<? extends Component> lines) { + this.lines.addAll(lines); + return this; + } + + @Override + public LabelOption build() { + MutableComponent text = Component.empty(); + Iterator<Component> iterator = lines.iterator(); + while (iterator.hasNext()) { + text.append(iterator.next()); + + if (iterator.hasNext()) + text.append("\n"); + } + + return new LabelOptionImpl(text); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java b/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java new file mode 100644 index 0000000..1cd5e55 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/ListOptionEntryImpl.java @@ -0,0 +1,154 @@ +package dev.isxander.yacl3.impl; + +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.AbstractWidget; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.ListEntryWidget; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +@ApiStatus.Internal +public final class ListOptionEntryImpl<T> implements ListOptionEntry<T> { + private final ListOptionImpl<T> group; + + private T value; + + private final Binding<T> binding; + private final Controller<T> controller; + + ListOptionEntryImpl(ListOptionImpl<T> group, T initialValue, @NotNull Function<ListOptionEntry<T>, Controller<T>> controlGetter) { + this.group = group; + this.value = initialValue; + this.binding = new EntryBinding(); + this.controller = new EntryController<>(controlGetter.apply(new HiddenNameListOptionEntry<>(this)), this); + } + + @Override + public @NotNull Component name() { + return group.name(); + } + + @Override + public @NotNull OptionDescription description() { + return group.description(); + } + + @Override + public @NotNull Component tooltip() { + return group.tooltip(); + } + + @Override + public @NotNull Controller<T> controller() { + return controller; + } + + @Override + public @NotNull Binding<T> binding() { + return binding; + } + + @Override + public boolean available() { + return parentGroup().available(); + } + + @Override + public void setAvailable(boolean available) { + + } + + @Override + public ListOption<T> parentGroup() { + return group; + } + + @Override + public boolean changed() { + return false; + } + + @Override + public @NotNull T pendingValue() { + return value; + } + + @Override + public void requestSet(@NotNull T value) { + binding.setValue(value); + } + + @Override + public boolean applyValue() { + return false; + } + + @Override + public void forgetPendingValue() { + + } + + @Override + public void requestSetDefault() { + + } + + @Override + public boolean isPendingValueDefault() { + return false; + } + + @Override + public boolean canResetToDefault() { + return false; + } + + @Override + public void addListener(BiConsumer<Option<T>, T> changedListener) { + + } + + /** + * Open in case mods need to find the real controller type. + */ + @ApiStatus.Internal + public record EntryController<T>(Controller<T> controller, ListOptionEntryImpl<T> entry) implements Controller<T> { + @Override + public Option<T> option() { + return controller.option(); + } + + @Override + public Component formatValue() { + return controller.formatValue(); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) { + return new ListEntryWidget(screen, entry, controller.provideWidget(screen, widgetDimension)); + } + } + + private class EntryBinding implements Binding<T> { + @Override + public void setValue(T newValue) { + value = newValue; + group.callListeners(true); + } + + @Override + public T getValue() { + return value; + } + + @Override + public T defaultValue() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java b/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java new file mode 100644 index 0000000..c77d55f --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/ListOptionImpl.java @@ -0,0 +1,402 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.impl.utils.YACLConstants; +import net.minecraft.network.chat.Component; +import org.apache.commons.lang3.Validate; +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; +import java.util.stream.Collectors; + +@ApiStatus.Internal +public final class ListOptionImpl<T> implements ListOption<T> { + private final Component name; + private final OptionDescription description; + private final Binding<List<T>> binding; + private final Supplier<T> initialValue; + private final List<ListOptionEntry<T>> entries; + private final boolean collapsed; + private boolean available; + private final int minimumNumberOfEntries; + private final int maximumNumberOfEntries; + private final boolean insertEntriesAtEnd; + private final ImmutableSet<OptionFlag> flags; + private final EntryFactory entryFactory; + + private final List<BiConsumer<Option<List<T>>, List<T>>> listeners; + private final List<Runnable> refreshListeners; + private int listenerTriggerDepth = 0; + + public ListOptionImpl(@NotNull Component name, @NotNull OptionDescription description, @NotNull Binding<List<T>> binding, @NotNull Supplier<T> initialValue, @NotNull Function<ListOptionEntry<T>, Controller<T>> controllerFunction, ImmutableSet<OptionFlag> flags, boolean collapsed, boolean available, int minimumNumberOfEntries, int maximumNumberOfEntries, boolean insertEntriesAtEnd, Collection<BiConsumer<Option<List<T>>, List<T>>> listeners) { + this.name = name; + this.description = description; + this.binding = new SafeBinding<>(binding); + this.initialValue = initialValue; + this.entryFactory = new EntryFactory(controllerFunction); + this.entries = createEntries(binding().getValue()); + this.collapsed = collapsed; + this.flags = flags; + this.available = available; + this.minimumNumberOfEntries = minimumNumberOfEntries; + this.maximumNumberOfEntries = maximumNumberOfEntries; + this.insertEntriesAtEnd = insertEntriesAtEnd; + this.listeners = new ArrayList<>(); + this.listeners.addAll(listeners); + this.refreshListeners = new ArrayList<>(); + callListeners(true); + } + + @Override + public @NotNull Component name() { + return this.name; + } + + @Override + public @NotNull OptionDescription description() { + return this.description; + } + + @Override + public @NotNull Component tooltip() { + return description().text(); + } + + @Override + public @NotNull ImmutableList<ListOptionEntry<T>> options() { + return ImmutableList.copyOf(entries); + } + + @Override + public @NotNull Controller<List<T>> controller() { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull Binding<List<T>> binding() { + return binding; + } + + @Override + public boolean collapsed() { + return collapsed; + } + + @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return flags; + } + + @Override + public @NotNull ImmutableList<T> pendingValue() { + return ImmutableList.copyOf(entries.stream().map(Option::pendingValue).toList()); + } + + @Override + public void insertEntry(int index, ListOptionEntry<?> entry) { + entries.add(index, (ListOptionEntry<T>) entry); + onRefresh(); + } + + @Override + public ListOptionEntry<T> insertNewEntry() { + ListOptionEntry<T> newEntry = entryFactory.create(initialValue.get()); + if (insertEntriesAtEnd) { + entries.add(newEntry); + } else { + // insert at top + entries.add(0, newEntry); + } + onRefresh(); + return newEntry; + } + + @Override + public void removeEntry(ListOptionEntry<?> entry) { + if (entries.remove(entry)) + onRefresh(); + } + + @Override + public int indexOf(ListOptionEntry<?> entry) { + return entries.indexOf(entry); + } + + @Override + public void requestSet(@NotNull List<T> value) { + entries.clear(); + entries.addAll(createEntries(value)); + onRefresh(); + } + + @Override + public boolean changed() { + return !binding().getValue().equals(pendingValue()); + } + + @Override + public boolean applyValue() { + if (changed()) { + binding().setValue(pendingValue()); + return true; + } + return false; + } + + @Override + public void forgetPendingValue() { + requestSet(binding().getValue()); + } + + @Override + public void requestSetDefault() { + requestSet(binding().defaultValue()); + } + + @Override + public boolean isPendingValueDefault() { + return binding().defaultValue().equals(pendingValue()); + } + + @Override + public boolean available() { + return available; + } + + @Override + public void setAvailable(boolean available) { + boolean changed = this.available != available; + + this.available = available; + + if (changed) + callListeners(false); + } + + @Override + public int numberOfEntries() { + return this.entries.size(); + } + @Override + public int maximumNumberOfEntries() { + return this.maximumNumberOfEntries; + } + @Override + public int minimumNumberOfEntries() { + return this.minimumNumberOfEntries; + } + + @Override + public void addListener(BiConsumer<Option<List<T>>, List<T>> changedListener) { + this.listeners.add(changedListener); + } + + @Override + public void addRefreshListener(Runnable changedListener) { + this.refreshListeners.add(changedListener); + } + + @Override + public boolean isRoot() { + return false; + } + + private List<ListOptionEntry<T>> createEntries(Collection<T> values) { + return values.stream().map(entryFactory::create).collect(Collectors.toList()); + } + + void callListeners(boolean bypass) { + List<T> pendingValue = pendingValue(); + if (bypass || listenerTriggerDepth == 0) { + if (listenerTriggerDepth > 10) { + throw new IllegalStateException("Listener trigger depth exceeded 10! This means a listener triggered a listener etc etc 10 times deep. This is likely a bug in the mod using YACL!"); + } + + this.listenerTriggerDepth++; + + for (BiConsumer<Option<List<T>>, List<T>> listener : listeners) { + try { + listener.accept(this, pendingValue); + } catch (Exception e) { + YACLConstants.LOGGER.error("Exception whilst triggering listener for option '%s'".formatted(name.getString()), e); + } + } + + this.listenerTriggerDepth--; + } + } + + private void onRefresh() { + refreshListeners.forEach(Runnable::run); + callListeners(true); + } + + private class EntryFactory { + private final Function<ListOptionEntry<T>, Controller<T>> controllerFunction; + + private EntryFactory(Function<ListOptionEntry<T>, Controller<T>> controllerFunction) { + this.controllerFunction = controllerFunction; + } + + public ListOptionEntry<T> create(T initialValue) { + return new ListOptionEntryImpl<>(ListOptionImpl.this, initialValue, controllerFunction); + } + } + + @ApiStatus.Internal + public static final class BuilderImpl<T> implements Builder<T> { + private Component name = Component.empty(); + private OptionDescription description = OptionDescription.EMPTY; + private Function<ListOptionEntry<T>, Controller<T>> controllerFunction; + private Binding<List<T>> binding = null; + private final Set<OptionFlag> flags = new HashSet<>(); + private Supplier<T> initialValue; + private boolean collapsed = false; + private boolean available = true; + private int minimumNumberOfEntries = 0; + private int maximumNumberOfEntries = Integer.MAX_VALUE; + private boolean insertEntriesAtEnd = false; + private final List<BiConsumer<Option<List<T>>, List<T>>> listeners = new ArrayList<>(); + + @Override + public Builder<T> name(@NotNull Component name) { + Validate.notNull(name, "`name` must not be null"); + + this.name = name; + return this; + } + + @Override + public Builder<T> description(@NotNull OptionDescription description) { + Validate.notNull(description, "`description` must not be null"); + + this.description = description; + return this; + } + + @Override + public Builder<T> initial(@NotNull Supplier<T> initialValue) { + Validate.notNull(initialValue, "`initialValue` cannot be empty"); + + this.initialValue = initialValue; + return this; + } + + @Override + public Builder<T> initial(@NotNull T initialValue) { + Validate.notNull(initialValue, "`initialValue` cannot be empty"); + + this.initialValue = () -> initialValue; + return this; + } + + @Override + public Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controller) { + Validate.notNull(controller, "`controller` cannot be null"); + + this.controllerFunction = opt -> controller.apply(opt).build(); + return this; + } + + @Override + public Builder<T> customController(@NotNull Function<ListOptionEntry<T>, Controller<T>> control) { + Validate.notNull(control, "`control` cannot be null"); + + this.controllerFunction = control; + return this; + } + + @Override + public Builder<T> binding(@NotNull Binding<List<T>> binding) { + Validate.notNull(binding, "`binding` cannot be null"); + + this.binding = binding; + return this; + } + + @Override + public Builder<T> binding(@NotNull List<T> def, @NotNull Supplier<@NotNull List<T>> getter, @NotNull Consumer<@NotNull List<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 Builder<T> available(boolean available) { + this.available = available; + return this; + } + + @Override + public Builder<T> minimumNumberOfEntries(int number) { + this.minimumNumberOfEntries = number; + return this; + } + + @Override + public Builder<T> maximumNumberOfEntries(int number) { + this.maximumNumberOfEntries = number; + return this; + } + + @Override + public Builder<T> insertEntriesAtEnd(boolean insertAtEnd) { + this.insertEntriesAtEnd = insertAtEnd; + return this; + } + + @Override + public Builder<T> flag(@NotNull OptionFlag... flag) { + Validate.notNull(flag, "`flag` must not be null"); + + this.flags.addAll(Arrays.asList(flag)); + return this; + } + + @Override + public Builder<T> flags(@NotNull Collection<OptionFlag> flags) { + Validate.notNull(flags, "`flags` must not be null"); + + this.flags.addAll(flags); + return this; + } + + @Override + public Builder<T> collapsed(boolean collapsible) { + this.collapsed = collapsible; + return this; + } + + @Override + public Builder<T> listener(@NotNull BiConsumer<Option<List<T>>, List<T>> listener) { + this.listeners.add(listener); + return this; + } + + @Override + public Builder<T> listeners(@NotNull Collection<BiConsumer<Option<List<T>>, List<T>>> listeners) { + this.listeners.addAll(listeners); + return this; + } + + @Override + public ListOption<T> 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"); + + return new ListOptionImpl<>(name, description, binding, initialValue, controllerFunction, ImmutableSet.copyOf(flags), collapsed, available, minimumNumberOfEntries, maximumNumberOfEntries, insertEntriesAtEnd, listeners); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/OptionDescriptionImpl.java b/src/main/java/dev/isxander/yacl3/impl/OptionDescriptionImpl.java new file mode 100644 index 0000000..67fa6a6 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/OptionDescriptionImpl.java @@ -0,0 +1,133 @@ +package dev.isxander.yacl3.impl; + +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.gui.image.ImageRenderer; +import dev.isxander.yacl3.gui.image.ImageRendererManager; +import dev.isxander.yacl3.gui.image.impl.AnimatedDynamicTextureImage; +import dev.isxander.yacl3.gui.image.impl.DynamicTextureImage; +import dev.isxander.yacl3.gui.image.impl.ResourceTextureImage; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import org.apache.commons.lang3.Validate; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public record OptionDescriptionImpl(Component text, CompletableFuture<Optional<ImageRenderer>> image) implements OptionDescription { + public static class BuilderImpl implements Builder { + private final List<Component> descriptionLines = new ArrayList<>(); + private CompletableFuture<Optional<ImageRenderer>> image = CompletableFuture.completedFuture(Optional.empty()); + private boolean imageUnset = true; + + @Override + public Builder text(Component... description) { + this.descriptionLines.addAll(Arrays.asList(description)); + return this; + } + + @Override + public Builder text(Collection<? extends Component> lines) { + this.descriptionLines.addAll(lines); + return this; + } + + @Override + public Builder image(ResourceLocation image, int width, int height) { + Validate.isTrue(imageUnset, "Image already set!"); + Validate.isTrue(width > 0, "Width must be greater than 0!"); + Validate.isTrue(height > 0, "Height must be greater than 0!"); + + this.image = ImageRendererManager.registerImage(image, ResourceTextureImage.createFactory(image, 0, 0, width, height, width, height)).thenApply(Optional::of); + imageUnset = false; + return this; + } + + @Override + public Builder image(ResourceLocation image, float u, float v, int width, int height, int textureWidth, int textureHeight) { + Validate.isTrue(imageUnset, "Image already set!"); + Validate.isTrue(width > 0, "Width must be greater than 0!"); + Validate.isTrue(height > 0, "Height must be greater than 0!"); + + this.image = ImageRendererManager.registerImage(image, ResourceTextureImage.createFactory(image, u, v, width, height, textureWidth, textureHeight)).thenApply(Optional::of); + imageUnset = false; + return this; + } + + @Override + public Builder image(Path path, ResourceLocation uniqueLocation) { + Validate.isTrue(imageUnset, "Image already set!"); + + this.image = ImageRendererManager.registerImage(uniqueLocation, DynamicTextureImage.fromPath(path, uniqueLocation)).thenApply(Optional::of); + imageUnset = false; + return this; + } + + @Override + public Builder gifImage(ResourceLocation image) { + Validate.isTrue(imageUnset, "Image already set!"); + + this.image = ImageRendererManager.registerImage(image, AnimatedDynamicTextureImage.createGIFFromTexture(image)).thenApply(Optional::of); + imageUnset = false; + return this; + } + + @Override + public Builder gifImage(Path path, ResourceLocation uniqueLocation) { + Validate.isTrue(imageUnset, "Image already set!"); + + this.image = ImageRendererManager.registerImage(uniqueLocation, AnimatedDynamicTextureImage.createGIFFromPath(path, uniqueLocation)).thenApply(Optional::of); + imageUnset = false; + return this; + } + + @Override + public Builder webpImage(ResourceLocation image) { + Validate.isTrue(imageUnset, "Image already set!"); + + Optional<ImageRenderer> completedImage = ImageRendererManager.getImage(image); + if (completedImage.isPresent()) { + this.image = CompletableFuture.completedFuture(completedImage); + } else { + this.image = ImageRendererManager.registerImage(image, AnimatedDynamicTextureImage.createWEBPFromTexture(image)).thenApply(Optional::of); + } + + imageUnset = false; + return this; + } + + @Override + public Builder webpImage(Path path, ResourceLocation uniqueLocation) { + Validate.isTrue(imageUnset, "Image already set!"); + + this.image = ImageRendererManager.registerImage(uniqueLocation, AnimatedDynamicTextureImage.createWEBPFromPath(path, uniqueLocation)).thenApply(Optional::of); + imageUnset = false; + return this; + } + + @Override + public Builder customImage(CompletableFuture<Optional<ImageRenderer>> image) { + Validate.notNull(image, "Image cannot be null!"); + Validate.isTrue(imageUnset, "Image already set!"); + + this.image = image; + this.imageUnset = false; + return this; + } + + @Override + public OptionDescription build() { + MutableComponent concatenatedDescription = Component.empty(); + Iterator<Component> iter = descriptionLines.iterator(); + while (iter.hasNext()) { + concatenatedDescription.append(iter.next()); + if (iter.hasNext()) concatenatedDescription.append("\n"); + } + + return new OptionDescriptionImpl(concatenatedDescription, image); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/OptionGroupImpl.java b/src/main/java/dev/isxander/yacl3/impl/OptionGroupImpl.java new file mode 100644 index 0000000..7805b29 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/OptionGroupImpl.java @@ -0,0 +1,121 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.api.ListOption; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.api.OptionGroup; +import net.minecraft.network.chat.Component; +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 final class OptionGroupImpl implements OptionGroup { + private final @NotNull Component name; + private final @NotNull OptionDescription description; + private final ImmutableList<? extends Option<?>> options; + private final boolean collapsed; + private final boolean isRoot; + + public OptionGroupImpl(@NotNull Component name, @NotNull OptionDescription description, ImmutableList<? extends Option<?>> options, boolean collapsed, boolean isRoot) { + this.name = name; + this.description = description; + this.options = options; + this.collapsed = collapsed; + this.isRoot = isRoot; + } + + @Override + public @NotNull Component name() { + return name; + } + + @Override + public OptionDescription description() { + return description; + } + + @Override + public @NotNull Component tooltip() { + return description.text(); + } + + @Override + public @NotNull ImmutableList<? extends Option<?>> options() { + return options; + } + + @Override + public boolean collapsed() { + return collapsed; + } + + @Override + public boolean isRoot() { + return isRoot; + } + + @ApiStatus.Internal + public static final class BuilderImpl implements Builder { + private Component name = Component.empty(); + private OptionDescription description = OptionDescription.EMPTY; + private final List<Option<?>> options = new ArrayList<>(); + private boolean collapsed = false; + + @Override + public Builder name(@NotNull Component name) { + Validate.notNull(name, "`name` must not be null"); + + this.name = name; + return this; + } + + @Override + public Builder description(@NotNull OptionDescription description) { + Validate.notNull(description, "`description` must not be null"); + + this.description = description; + return this; + } + + @Override + public 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 Builder options(@NotNull Collection<? extends Option<?>> 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 Builder collapsed(boolean collapsible) { + this.collapsed = collapsible; + return this; + } + + @Override + public OptionGroup build() { + Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`"); + + return new OptionGroupImpl(name, description, ImmutableList.copyOf(options), collapsed, false); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java b/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java new file mode 100644 index 0000000..afe9517 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/OptionImpl.java @@ -0,0 +1,295 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.api.controller.ControllerBuilder; +import dev.isxander.yacl3.impl.utils.YACLConstants; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import org.apache.commons.lang3.Validate; +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; + +@ApiStatus.Internal +public final class OptionImpl<T> implements Option<T> { + private final Component name; + private OptionDescription description; + private final Controller<T> controller; + private final Binding<T> binding; + private boolean available; + + private final ImmutableSet<OptionFlag> flags; + + private T pendingValue; + + private final List<BiConsumer<Option<T>, T>> listeners; + private int listenerTriggerDepth = 0; + + public OptionImpl( + @NotNull Component name, + @NotNull Function<T, OptionDescription> descriptionFunction, + @NotNull Function<Option<T>, Controller<T>> controlGetter, + @NotNull Binding<T> binding, + boolean available, + ImmutableSet<OptionFlag> flags, + @NotNull Collection<BiConsumer<Option<T>, T>> listeners + ) { + this.name = name; + this.binding = new SafeBinding<>(binding); + this.available = available; + this.flags = flags; + this.listeners = new ArrayList<>(listeners); + + this.pendingValue = binding.getValue(); + this.controller = controlGetter.apply(this); + + addListener((opt, pending) -> description = descriptionFunction.apply(pending)); + triggerListeners(true); + } + + @Override + public @NotNull Component name() { + return name; + } + + @Override + public @NotNull OptionDescription description() { + return this.description; + } + + @Override + public @NotNull Component tooltip() { + return description.text(); + } + + @Override + public @NotNull Controller<T> controller() { + return controller; + } + + @Override + public @NotNull Binding<T> binding() { + return binding; + } + + @Override + public boolean available() { + return available; + } + + @Override + public void setAvailable(boolean available) { + boolean changed = this.available != available; + + this.available = available; + + if (changed) { + if (!available) { + this.pendingValue = binding().getValue(); + } + this.triggerListeners(!available); + } + } + + @Override + public @NotNull ImmutableSet<OptionFlag> flags() { + return flags; + } + + @Override + public boolean changed() { + return !binding().getValue().equals(pendingValue); + } + + @Override + public @NotNull T pendingValue() { + return pendingValue; + } + + @Override + public void requestSet(@NotNull T value) { + Validate.notNull(value, "`value` cannot be null"); + + pendingValue = value; + this.triggerListeners(true); + } + + @Override + public boolean applyValue() { + if (changed()) { + binding().setValue(pendingValue); + return true; + } + return false; + } + + @Override + public void forgetPendingValue() { + requestSet(binding().getValue()); + } + + @Override + public void requestSetDefault() { + requestSet(binding().defaultValue()); + } + + @Override + public boolean isPendingValueDefault() { + return binding().defaultValue().equals(pendingValue()); + } + + @Override + public void addListener(BiConsumer<Option<T>, T> changedListener) { + this.listeners.add(changedListener); + } + + private void triggerListeners(boolean bypass) { + if (bypass || listenerTriggerDepth == 0) { + if (listenerTriggerDepth > 10) { + throw new IllegalStateException("Listener trigger depth exceeded 10! This means a listener triggered a listener etc etc 10 times deep. This is likely a bug in the mod using YACL!"); + } + + this.listenerTriggerDepth++; + + for (BiConsumer<Option<T>, T> listener : listeners) { + try { + listener.accept(this, pendingValue); + } catch (Exception e) { + YACLConstants.LOGGER.error("Exception whilst triggering listener for option '%s'".formatted(name.getString()), e); + } + } + + this.listenerTriggerDepth--; + } + } + + @ApiStatus.Internal + public static class BuilderImpl<T> implements Builder<T> { + private Component name = Component.literal("Name not specified!").withStyle(ChatFormatting.RED); + + private Function<T, OptionDescription> descriptionFunction = pending -> OptionDescription.EMPTY; + + private Function<Option<T>, Controller<T>> controlGetter; + + private Binding<T> binding; + + private boolean available = true; + + private boolean instant = false; + + private final Set<OptionFlag> flags = new HashSet<>(); + + private final List<BiConsumer<Option<T>, T>> listeners = new ArrayList<>(); + + @Override + public Builder<T> name(@NotNull Component name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public Builder<T> description(@NotNull OptionDescription description) { + return description(opt -> description); + } + + @Override + public Builder<T> description(@NotNull Function<T, OptionDescription> descriptionFunction) { + this.descriptionFunction = descriptionFunction; + return this; + } + + @Override + public Builder<T> controller(@NotNull Function<Option<T>, ControllerBuilder<T>> controllerBuilder) { + Validate.notNull(controllerBuilder, "`controllerBuilder` cannot be null"); + + return customController(opt -> controllerBuilder.apply(opt).build()); + } + + @Override + public Builder<T> customController(@NotNull Function<Option<T>, Controller<T>> control) { + Validate.notNull(control, "`control` cannot be null"); + + this.controlGetter = control; + return this; + } + + @Override + public Builder<T> binding(@NotNull Binding<T> binding) { + Validate.notNull(binding, "`binding` cannot be null"); + + this.binding = binding; + return this; + } + + @Override + public Builder<T> 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 Builder<T> available(boolean available) { + this.available = available; + return this; + } + + @Override + public Builder<T> flag(@NotNull OptionFlag... flag) { + Validate.notNull(flag, "`flag` must not be null"); + + this.flags.addAll(Arrays.asList(flag)); + return this; + } + + @Override + public Builder<T> flags(@NotNull Collection<? extends OptionFlag> flags) { + Validate.notNull(flags, "`flags` must not be null"); + + this.flags.addAll(flags); + return this; + } + + @Override + public Builder<T> instant(boolean instant) { + this.instant = instant; + return this; + } + + @Override + public Builder<T> listener(@NotNull BiConsumer<Option<T>, T> listener) { + this.listeners.add(listener); + return this; + } + + @Override + public Builder<T> listeners(@NotNull Collection<BiConsumer<Option<T>, T>> listeners) { + this.listeners.addAll(listeners); + return this; + } + + @Override + public Option<T> 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"); + + if (instant) { + listeners.add((opt, pendingValue) -> opt.applyValue()); + } + + return new OptionImpl<>(name, descriptionFunction, controlGetter, binding, available, ImmutableSet.copyOf(flags), listeners); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/PlaceholderCategoryImpl.java b/src/main/java/dev/isxander/yacl3/impl/PlaceholderCategoryImpl.java new file mode 100644 index 0000000..5e836a3 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/PlaceholderCategoryImpl.java @@ -0,0 +1,99 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.PlaceholderCategory; +import dev.isxander.yacl3.gui.YACLScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +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 final class PlaceholderCategoryImpl implements PlaceholderCategory { + private final Component name; + private final BiFunction<Minecraft, YACLScreen, Screen> screen; + private final Component tooltip; + + public PlaceholderCategoryImpl(Component name, BiFunction<Minecraft, YACLScreen, Screen> screen, Component tooltip) { + this.name = name; + this.screen = screen; + this.tooltip = tooltip; + } + + @Override + public @NotNull ImmutableList<OptionGroup> groups() { + return ImmutableList.of(); + } + + @Override + public @NotNull Component name() { + return name; + } + + @Override + public BiFunction<Minecraft, YACLScreen, Screen> screen() { + return screen; + } + + @Override + public @NotNull Component tooltip() { + return tooltip; + } + + @ApiStatus.Internal + public static final class BuilderImpl implements Builder { + private Component name; + + private final List<Component> tooltipLines = new ArrayList<>(); + + private BiFunction<Minecraft, YACLScreen, Screen> screenFunction; + + @Override + public Builder name(@NotNull Component name) { + Validate.notNull(name, "`name` cannot be null"); + + this.name = name; + return this; + } + + @Override + public Builder tooltip(@NotNull Component... tooltips) { + Validate.notEmpty(tooltips, "`tooltips` cannot be empty"); + + tooltipLines.addAll(List.of(tooltips)); + return this; + } + + @Override + public Builder screen(@NotNull BiFunction<Minecraft, YACLScreen, Screen> 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`"); + + MutableComponent concatenatedTooltip = Component.empty(); + boolean first = true; + for (Component line : tooltipLines) { + if (!first) concatenatedTooltip.append("\n"); + first = false; + + concatenatedTooltip.append(line); + } + + return new PlaceholderCategoryImpl(name, screenFunction, concatenatedTooltip); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/SafeBinding.java b/src/main/java/dev/isxander/yacl3/impl/SafeBinding.java new file mode 100644 index 0000000..c55d2be --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/SafeBinding.java @@ -0,0 +1,29 @@ +package dev.isxander.yacl3.impl; + +import dev.isxander.yacl3.api.Binding; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class SafeBinding<T> implements Binding<T> { + private final Binding<T> binding; + + public SafeBinding(Binding<T> binding) { + this.binding = binding; + } + + @Override + public @NotNull T getValue() { + return Objects.requireNonNull(binding.getValue()); + } + + @Override + public void setValue(@NotNull T value) { + binding.setValue(Objects.requireNonNull(value)); + } + + @Override + public @NotNull T defaultValue() { + return Objects.requireNonNull(binding.defaultValue()); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/YetAnotherConfigLibImpl.java b/src/main/java/dev/isxander/yacl3/impl/YetAnotherConfigLibImpl.java new file mode 100644 index 0000000..0be02a7 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/YetAnotherConfigLibImpl.java @@ -0,0 +1,122 @@ +package dev.isxander.yacl3.impl; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.api.ConfigCategory; +import dev.isxander.yacl3.api.PlaceholderCategory; +import dev.isxander.yacl3.api.YetAnotherConfigLib; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.impl.utils.YACLConstants; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +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.function.Consumer; + +@ApiStatus.Internal +public final class YetAnotherConfigLibImpl implements YetAnotherConfigLib { + private final Component title; + private final ImmutableList<ConfigCategory> categories; + private final Runnable saveFunction; + private final Consumer<YACLScreen> initConsumer; + + private boolean generated = false; + + public YetAnotherConfigLibImpl(Component title, ImmutableList<ConfigCategory> categories, Runnable saveFunction, Consumer<YACLScreen> initConsumer) { + this.title = title; + this.categories = categories; + this.saveFunction = saveFunction; + this.initConsumer = initConsumer; + } + + @Override + public Screen generateScreen(Screen parent) { + if (generated) + throw new UnsupportedOperationException("To prevent memory leaks, you should only generate a Screen once per instance. Please re-build the instance to generate another GUI."); + + YACLConstants.LOGGER.info("Generating YACL screen"); + generated = true; + return new YACLScreen(this, parent); + } + + @Override + public Component title() { + return title; + } + + @Override + public ImmutableList<ConfigCategory> categories() { + return categories; + } + + @Override + public Runnable saveFunction() { + return saveFunction; + } + + @Override + public Consumer<YACLScreen> initConsumer() { + return initConsumer; + } + + @ApiStatus.Internal + public static final class BuilderImpl implements Builder { + private Component title; + private final List<ConfigCategory> categories = new ArrayList<>(); + private Runnable saveFunction = () -> {}; + private Consumer<YACLScreen> initConsumer = screen -> {}; + + @Override + public Builder title(@NotNull Component title) { + Validate.notNull(title, "`title` cannot be null"); + + this.title = title; + return this; + } + + @Override + public Builder category(@NotNull ConfigCategory category) { + Validate.notNull(category, "`category` cannot be null"); + + this.categories.add(category); + return this; + } + + @Override + public Builder categories(@NotNull Collection<? extends ConfigCategory> categories) { + Validate.notNull(categories, "`categories` cannot be null"); + + this.categories.addAll(categories); + return this; + } + + @Override + public Builder save(@NotNull Runnable saveFunction) { + Validate.notNull(saveFunction, "`saveFunction` cannot be null"); + + this.saveFunction = saveFunction; + return this; + } + + @Override + public Builder screenInit(@NotNull Consumer<YACLScreen> 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); + } + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/AbstractControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/AbstractControllerBuilderImpl.java new file mode 100644 index 0000000..66c025a --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/AbstractControllerBuilderImpl.java @@ -0,0 +1,12 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ControllerBuilder; + +public abstract class AbstractControllerBuilderImpl<T> implements ControllerBuilder<T> { + protected final Option<T> option; + + protected AbstractControllerBuilderImpl(Option<T> option) { + this.option = option; + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/BooleanControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/BooleanControllerBuilderImpl.java new file mode 100644 index 0000000..063a177 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/BooleanControllerBuilderImpl.java @@ -0,0 +1,57 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.BooleanController; +import net.minecraft.network.chat.Component; +import org.apache.commons.lang3.Validate; + +import java.util.function.Function; + +public class BooleanControllerBuilderImpl extends AbstractControllerBuilderImpl<Boolean> implements BooleanControllerBuilder { + private boolean coloured = false; + private ValueFormatter<Boolean> formatter = BooleanController.ON_OFF_FORMATTER::apply; + + public BooleanControllerBuilderImpl(Option<Boolean> option) { + super(option); + } + + @Override + public BooleanControllerBuilder coloured(boolean coloured) { + this.coloured = coloured; + return this; + } + + @Override + public BooleanControllerBuilder formatValue(ValueFormatter<Boolean> formatter) { + Validate.notNull(formatter, "formatter cannot be null"); + + this.formatter = formatter; + return this; + } + + @Override + public BooleanControllerBuilder onOffFormatter() { + this.formatter = BooleanController.ON_OFF_FORMATTER::apply; + return this; + } + + @Override + public BooleanControllerBuilder yesNoFormatter() { + this.formatter = BooleanController.YES_NO_FORMATTER::apply; + return this; + } + + @Override + public BooleanControllerBuilder trueFalseFormatter() { + this.formatter = BooleanController.TRUE_FALSE_FORMATTER::apply; + return this; + } + + @Override + public Controller<Boolean> build() { + return BooleanController.createInternal(option, formatter, coloured); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/ColorControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/ColorControllerBuilderImpl.java new file mode 100644 index 0000000..9412165 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/ColorControllerBuilderImpl.java @@ -0,0 +1,27 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ColorControllerBuilder; +import dev.isxander.yacl3.gui.controllers.ColorController; + +import java.awt.Color; + +public class ColorControllerBuilderImpl extends AbstractControllerBuilderImpl<Color> implements ColorControllerBuilder { + private boolean allowAlpha = false; + + public ColorControllerBuilderImpl(Option<Color> option) { + super(option); + } + + @Override + public ColorControllerBuilder allowAlpha(boolean allowAlpha) { + this.allowAlpha = allowAlpha; + return this; + } + + @Override + public Controller<Color> build() { + return new ColorController(option, allowAlpha); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/CyclingListControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/CyclingListControllerBuilderImpl.java new file mode 100644 index 0000000..8e2e481 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/CyclingListControllerBuilderImpl.java @@ -0,0 +1,41 @@ +package dev.isxander.yacl3.impl.controller; + +import com.google.common.collect.ImmutableList; +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.CyclingListControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.cycling.CyclingListController; + +public final class CyclingListControllerBuilderImpl<T> extends AbstractControllerBuilderImpl<T> implements CyclingListControllerBuilder<T> { + private Iterable<? extends T> values; + private ValueFormatter<T> formatter = null; + + public CyclingListControllerBuilderImpl(Option<T> option) { + super(option); + } + + @Override + public CyclingListControllerBuilder<T> values(Iterable<? extends T> values) { + this.values = values; + return this; + } + + @SafeVarargs + @Override + public final CyclingListControllerBuilder<T> values(T... values) { + this.values = ImmutableList.copyOf(values); + return this; + } + + @Override + public CyclingListControllerBuilder<T> formatValue(ValueFormatter<T> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<T> build() { + return CyclingListController.createInternal(option, values, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/DoubleFieldControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/DoubleFieldControllerBuilderImpl.java new file mode 100644 index 0000000..8d84e7d --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/DoubleFieldControllerBuilderImpl.java @@ -0,0 +1,51 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.DoubleFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.DoubleSliderController; +import dev.isxander.yacl3.gui.controllers.string.number.DoubleFieldController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class DoubleFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Double> implements DoubleFieldControllerBuilder { + private double min = Double.MIN_VALUE; + private double max = Double.MAX_VALUE; + private ValueFormatter<Double> formatter = DoubleSliderController.DEFAULT_FORMATTER::apply; + + public DoubleFieldControllerBuilderImpl(Option<Double> option) { + super(option); + } + + @Override + public DoubleFieldControllerBuilder min(Double min) { + this.min = min; + return this; + } + + @Override + public DoubleFieldControllerBuilder max(Double max) { + this.max = max; + return this; + } + + @Override + public DoubleFieldControllerBuilder range(Double min, Double max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public DoubleFieldControllerBuilder formatValue(ValueFormatter<Double> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Double> build() { + return DoubleFieldController.createInternal(option, min, max, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/DoubleSliderControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/DoubleSliderControllerBuilderImpl.java new file mode 100644 index 0000000..b696d57 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/DoubleSliderControllerBuilderImpl.java @@ -0,0 +1,44 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.DoubleSliderController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class DoubleSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Double> implements DoubleSliderControllerBuilder { + private double min, max; + private double step; + private ValueFormatter<Double> formatter = DoubleSliderController.DEFAULT_FORMATTER::apply; + + public DoubleSliderControllerBuilderImpl(Option<Double> option) { + super(option); + } + + @Override + public DoubleSliderControllerBuilder range(Double min, Double max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public DoubleSliderControllerBuilder step(Double step) { + this.step = step; + return this; + } + + @Override + public DoubleSliderControllerBuilder formatValue(ValueFormatter<Double> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Double> build() { + return DoubleSliderController.createInternal(option, min, max, step, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/DropdownStringControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/DropdownStringControllerBuilderImpl.java new file mode 100644 index 0000000..b300a6a --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/DropdownStringControllerBuilderImpl.java @@ -0,0 +1,49 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.DropdownStringControllerBuilder; +import dev.isxander.yacl3.gui.controllers.dropdown.DropdownStringController; + +import java.util.Arrays; +import java.util.List; + +public class DropdownStringControllerBuilderImpl extends StringControllerBuilderImpl implements DropdownStringControllerBuilder { + private List<String> values; + private boolean allowEmptyValue = false; + private boolean allowAnyValue = false; + + public DropdownStringControllerBuilderImpl(Option<String> option) { + super(option); + } + + @Override + public DropdownStringControllerBuilder values(List<String> values) { + this.values = values; + return this; + } + + @Override + public DropdownStringControllerBuilderImpl values(String... values) { + this.values = Arrays.asList(values); + return this; + } + + @Override + public DropdownStringControllerBuilderImpl allowEmptyValue(boolean allowEmptyValue) { + this.allowEmptyValue = allowEmptyValue; + return this; + } + + @Override + public DropdownStringControllerBuilderImpl allowAnyValue(boolean allowAnyValue) { + this.allowAnyValue = allowAnyValue; + return this; + } + + @Override + public Controller<String> build() { + return new DropdownStringController(option, values, allowEmptyValue, allowAnyValue); + } + +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/EnumControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/EnumControllerBuilderImpl.java new file mode 100644 index 0000000..04ee2a0 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/EnumControllerBuilderImpl.java @@ -0,0 +1,42 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.EnumControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.cycling.EnumController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class EnumControllerBuilderImpl<T extends Enum<T>> extends AbstractControllerBuilderImpl<T> implements EnumControllerBuilder<T> { + private Class<T> enumClass; + private ValueFormatter<T> formatter = null; + + public EnumControllerBuilderImpl(Option<T> option) { + super(option); + } + + @Override + public EnumControllerBuilder<T> enumClass(Class<T> enumClass) { + this.enumClass = enumClass; + return this; + } + + @Override + public EnumControllerBuilder<T> formatValue(ValueFormatter<T> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<T> build() { + ValueFormatter<T> formatter = this.formatter; + if (formatter == null) { + Function<T, Component> formatFunction = EnumController.getDefaultFormatter(); + formatter = formatFunction::apply; + } + + return EnumController.createInternal(option, formatter, enumClass.getEnumConstants()); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/EnumDropdownControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/EnumDropdownControllerBuilderImpl.java new file mode 100644 index 0000000..4ac063f --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/EnumDropdownControllerBuilderImpl.java @@ -0,0 +1,27 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.EnumDropdownControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.cycling.EnumController; +import dev.isxander.yacl3.gui.controllers.dropdown.EnumDropdownController; + +public class EnumDropdownControllerBuilderImpl<E extends Enum<E>> extends AbstractControllerBuilderImpl<E> implements EnumDropdownControllerBuilder<E> { + private ValueFormatter<E> formatter = EnumController.<E>getDefaultFormatter()::apply; + + public EnumDropdownControllerBuilderImpl(Option<E> option) { + super(option); + } + + @Override + public EnumDropdownControllerBuilder<E> formatValue(ValueFormatter<E> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<E> build() { + return new EnumDropdownController<>(option, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/FloatFieldControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/FloatFieldControllerBuilderImpl.java new file mode 100644 index 0000000..08fefd0 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/FloatFieldControllerBuilderImpl.java @@ -0,0 +1,51 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.FloatSliderController; +import dev.isxander.yacl3.gui.controllers.string.number.FloatFieldController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class FloatFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Float> implements FloatFieldControllerBuilder { + private float min = Float.MIN_VALUE; + private float max = Float.MAX_VALUE; + private ValueFormatter<Float> formatter = FloatSliderController.DEFAULT_FORMATTER::apply; + + public FloatFieldControllerBuilderImpl(Option<Float> option) { + super(option); + } + + @Override + public FloatFieldControllerBuilder min(Float min) { + this.min = min; + return this; + } + + @Override + public FloatFieldControllerBuilder max(Float max) { + this.max = max; + return this; + } + + @Override + public FloatFieldControllerBuilder range(Float min, Float max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public FloatFieldControllerBuilder formatValue(ValueFormatter<Float> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Float> build() { + return FloatFieldController.createInternal(option, min, max, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/FloatSliderControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/FloatSliderControllerBuilderImpl.java new file mode 100644 index 0000000..9b2d75b --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/FloatSliderControllerBuilderImpl.java @@ -0,0 +1,44 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.FloatSliderController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class FloatSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Float> implements FloatSliderControllerBuilder { + private float min, max; + private float step; + private ValueFormatter<Float> formatter = FloatSliderController.DEFAULT_FORMATTER::apply; + + public FloatSliderControllerBuilderImpl(Option<Float> option) { + super(option); + } + + @Override + public FloatSliderControllerBuilder range(Float min, Float max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public FloatSliderControllerBuilder step(Float step) { + this.step = step; + return this; + } + + @Override + public FloatSliderControllerBuilder formatValue(ValueFormatter<Float> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Float> build() { + return FloatSliderController.createInternal(option, min, max, step, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/IntegerFieldControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/IntegerFieldControllerBuilderImpl.java new file mode 100644 index 0000000..1435c49 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/IntegerFieldControllerBuilderImpl.java @@ -0,0 +1,51 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.IntegerSliderController; +import dev.isxander.yacl3.gui.controllers.string.number.IntegerFieldController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class IntegerFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Integer> implements IntegerFieldControllerBuilder { + private int min = Integer.MIN_VALUE; + private int max = Integer.MAX_VALUE; + private ValueFormatter<Integer> formatter = IntegerSliderController.DEFAULT_FORMATTER::apply; + + public IntegerFieldControllerBuilderImpl(Option<Integer> option) { + super(option); + } + + @Override + public IntegerFieldControllerBuilder min(Integer min) { + this.min = min; + return this; + } + + @Override + public IntegerFieldControllerBuilder max(Integer max) { + this.max = max; + return this; + } + + @Override + public IntegerFieldControllerBuilder range(Integer min, Integer max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public IntegerFieldControllerBuilder formatValue(ValueFormatter<Integer> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Integer> build() { + return IntegerFieldController.createInternal(option, min, max, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/IntegerSliderControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/IntegerSliderControllerBuilderImpl.java new file mode 100644 index 0000000..b9395a0 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/IntegerSliderControllerBuilderImpl.java @@ -0,0 +1,44 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.IntegerSliderController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class IntegerSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Integer> implements IntegerSliderControllerBuilder { + private int min, max; + private int step; + private ValueFormatter<Integer> formatter = IntegerSliderController.DEFAULT_FORMATTER::apply; + + public IntegerSliderControllerBuilderImpl(Option<Integer> option) { + super(option); + } + + @Override + public IntegerSliderControllerBuilder range(Integer min, Integer max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public IntegerSliderControllerBuilder step(Integer step) { + this.step = step; + return this; + } + + @Override + public IntegerSliderControllerBuilder formatValue(ValueFormatter<Integer> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Integer> build() { + return IntegerSliderController.createInternal(option, min, max, step, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/ItemControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/ItemControllerBuilderImpl.java new file mode 100644 index 0000000..9a817fb --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/ItemControllerBuilderImpl.java @@ -0,0 +1,18 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ItemControllerBuilder; +import dev.isxander.yacl3.gui.controllers.dropdown.ItemController; +import net.minecraft.world.item.Item; + +public class ItemControllerBuilderImpl extends AbstractControllerBuilderImpl<Item> implements ItemControllerBuilder { + public ItemControllerBuilderImpl(Option<Item> option) { + super(option); + } + + @Override + public Controller<Item> build() { + return new ItemController(option); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/LongFieldControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/LongFieldControllerBuilderImpl.java new file mode 100644 index 0000000..c7a3ea4 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/LongFieldControllerBuilderImpl.java @@ -0,0 +1,51 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.LongFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.LongSliderController; +import dev.isxander.yacl3.gui.controllers.string.number.LongFieldController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class LongFieldControllerBuilderImpl extends AbstractControllerBuilderImpl<Long> implements LongFieldControllerBuilder { + private long min = Long.MIN_VALUE; + private long max = Long.MAX_VALUE; + private ValueFormatter<Long> formatter = LongSliderController.DEFAULT_FORMATTER::apply; + + public LongFieldControllerBuilderImpl(Option<Long> option) { + super(option); + } + + @Override + public LongFieldControllerBuilder min(Long min) { + this.min = min; + return this; + } + + @Override + public LongFieldControllerBuilder max(Long max) { + this.max = max; + return this; + } + + @Override + public LongFieldControllerBuilder range(Long min, Long max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public LongFieldControllerBuilder formatValue(ValueFormatter<Long> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Long> build() { + return LongFieldController.createInternal(option, min, max, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/LongSliderControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/LongSliderControllerBuilderImpl.java new file mode 100644 index 0000000..5eda424 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/LongSliderControllerBuilderImpl.java @@ -0,0 +1,44 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.LongSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import dev.isxander.yacl3.gui.controllers.slider.LongSliderController; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public class LongSliderControllerBuilderImpl extends AbstractControllerBuilderImpl<Long> implements LongSliderControllerBuilder { + private long min, max; + private long step; + private ValueFormatter<Long> formatter = LongSliderController.DEFAULT_FORMATTER::apply; + + public LongSliderControllerBuilderImpl(Option<Long> option) { + super(option); + } + + @Override + public LongSliderControllerBuilder range(Long min, Long max) { + this.min = min; + this.max = max; + return this; + } + + @Override + public LongSliderControllerBuilder step(Long step) { + this.step = step; + return this; + } + + @Override + public LongSliderControllerBuilder formatValue(ValueFormatter<Long> formatter) { + this.formatter = formatter; + return this; + } + + @Override + public Controller<Long> build() { + return LongSliderController.createInternal(option, min, max, step, formatter); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/StringControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/StringControllerBuilderImpl.java new file mode 100644 index 0000000..a0f51b9 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/StringControllerBuilderImpl.java @@ -0,0 +1,17 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.StringControllerBuilder; +import dev.isxander.yacl3.gui.controllers.string.StringController; + +public class StringControllerBuilderImpl extends AbstractControllerBuilderImpl<String> implements StringControllerBuilder { + public StringControllerBuilderImpl(Option<String> option) { + super(option); + } + + @Override + public Controller<String> build() { + return new StringController(option); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/controller/TickBoxControllerBuilderImpl.java b/src/main/java/dev/isxander/yacl3/impl/controller/TickBoxControllerBuilderImpl.java new file mode 100644 index 0000000..3b29719 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/controller/TickBoxControllerBuilderImpl.java @@ -0,0 +1,17 @@ +package dev.isxander.yacl3.impl.controller; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder; +import dev.isxander.yacl3.gui.controllers.TickBoxController; + +public class TickBoxControllerBuilderImpl extends AbstractControllerBuilderImpl<Boolean> implements TickBoxControllerBuilder { + public TickBoxControllerBuilderImpl(Option<Boolean> option) { + super(option); + } + + @Override + public Controller<Boolean> build() { + return new TickBoxController(option); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/utils/DimensionIntegerImpl.java b/src/main/java/dev/isxander/yacl3/impl/utils/DimensionIntegerImpl.java new file mode 100644 index 0000000..7d29bbc --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/utils/DimensionIntegerImpl.java @@ -0,0 +1,115 @@ +package dev.isxander.yacl3.impl.utils; + +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.api.utils.MutableDimension; + +public class DimensionIntegerImpl implements MutableDimension<Integer> { + private int x, y; + private int width, height; + + public DimensionIntegerImpl(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public Integer x() { + return x; + } + + @Override + public Integer y() { + return y; + } + + @Override + public Integer width() { + return width; + } + + @Override + public Integer height() { + return height; + } + + @Override + public Integer xLimit() { + return x + width; + } + + @Override + public Integer yLimit() { + return y + height; + } + + @Override + public Integer centerX() { + return x + width / 2; + } + + @Override + public Integer centerY() { + return y + height / 2; + } + + @Override + public boolean isPointInside(Integer x, Integer y) { + return x >= x() && x <= xLimit() && y >= y() && y <= yLimit(); + } + + @Override + public MutableDimension<Integer> clone() { + return new DimensionIntegerImpl(x, y, width, height); + } + + @Override public MutableDimension<Integer> setX(Integer x) { this.x = x; return this; } + @Override public MutableDimension<Integer> setY(Integer y) { this.y = y; return this; } + @Override public MutableDimension<Integer> setWidth(Integer width) { this.width = width; return this; } + @Override public MutableDimension<Integer> setHeight(Integer height) { this.height = height; return this; } + + @Override + public Dimension<Integer> withX(Integer x) { + return clone().setX(x); + } + + @Override + public Dimension<Integer> withY(Integer y) { + return clone().setY(y); + } + + @Override + public Dimension<Integer> withWidth(Integer width) { + return clone().setWidth(width); + } + + @Override + public Dimension<Integer> withHeight(Integer height) { + return clone().setHeight(height); + } + + @Override + public MutableDimension<Integer> move(Integer x, Integer y) { + this.x += x; + this.y += y; + return this; + } + + @Override + public MutableDimension<Integer> expand(Integer width, Integer height) { + this.width += width; + this.height += height; + return this; + } + + @Override + public Dimension<Integer> moved(Integer x, Integer y) { + return clone().move(x, y); + } + + @Override + public Dimension<Integer> expanded(Integer width, Integer height) { + return clone().expand(width, height); + } +} diff --git a/src/main/java/dev/isxander/yacl3/impl/utils/YACLConstants.java b/src/main/java/dev/isxander/yacl3/impl/utils/YACLConstants.java new file mode 100644 index 0000000..5ff1b79 --- /dev/null +++ b/src/main/java/dev/isxander/yacl3/impl/utils/YACLConstants.java @@ -0,0 +1,8 @@ +package dev.isxander.yacl3.impl.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class YACLConstants { + public static final Logger LOGGER = LoggerFactory.getLogger("YetAnotherConfigLib"); +} |