From dd65110f60aa3e32c2970863a06a7682520cce5e Mon Sep 17 00:00:00 2001 From: Xander Date: Sun, 11 Dec 2022 19:31:56 +0000 Subject: [Feature] Lists (#40) --- .../dev/isxander/yacl/impl/ButtonOptionImpl.java | 5 - .../isxander/yacl/impl/ListOptionEntryImpl.java | 122 ++++++++++++ .../dev/isxander/yacl/impl/ListOptionImpl.java | 208 +++++++++++++++++++++ .../dev/isxander/yacl/impl/OptionGroupImpl.java | 2 +- .../java/dev/isxander/yacl/impl/OptionImpl.java | 5 - .../yacl/impl/YetAnotherConfigLibImpl.java | 67 ++++++- 6 files changed, 397 insertions(+), 12 deletions(-) create mode 100644 src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java create mode 100644 src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java (limited to 'src/client/java/dev/isxander/yacl/impl') diff --git a/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java b/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java index dcb9c7a..f526d42 100644 --- a/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/ButtonOptionImpl.java @@ -78,11 +78,6 @@ public class ButtonOptionImpl implements ButtonOption { return ImmutableSet.of(); } - @Override - public boolean requiresRestart() { - return false; - } - @Override public boolean changed() { return false; diff --git a/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java b/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java new file mode 100644 index 0000000..dc7aa88 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/impl/ListOptionEntryImpl.java @@ -0,0 +1,122 @@ +package dev.isxander.yacl.impl; + +import dev.isxander.yacl.api.*; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class ListOptionEntryImpl implements ListOptionEntry { + private final ListOptionImpl group; + + private T value; + + private final Binding binding; + private final Controller controller; + + public ListOptionEntryImpl(ListOptionImpl group, T initialValue, @NotNull Function, Controller> controlGetter) { + this.group = group; + this.value = initialValue; + this.binding = new EntryBinding(); + this.controller = controlGetter.apply(this); + } + + @Override + public @NotNull Text name() { + return Text.empty(); + } + + @Override + public @NotNull Text tooltip() { + return Text.empty(); + } + + @Override + public @NotNull Controller controller() { + return controller; + } + + @Override + public @NotNull Binding binding() { + return binding; + } + + @Override + public boolean available() { + return parentGroup().available(); + } + + @Override + public void setAvailable(boolean available) { + + } + + @Override + public ListOption parentGroup() { + return group; + } + + @Override + public boolean changed() { + return false; + } + + @Override + public @NotNull T pendingValue() { + return value; + } + + @Override + public void requestSet(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, T> changedListener) { + + } + + private class EntryBinding implements Binding { + @Override + public void setValue(T newValue) { + value = newValue; + group.callListeners(); + } + + @Override + public T getValue() { + return value; + } + + @Override + public T defaultValue() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java b/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java new file mode 100644 index 0000000..128d3e7 --- /dev/null +++ b/src/client/java/dev/isxander/yacl/impl/ListOptionImpl.java @@ -0,0 +1,208 @@ +package dev.isxander.yacl.impl; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import dev.isxander.yacl.api.*; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ListOptionImpl implements ListOption { + private final Text name; + private final Text tooltip; + private final Binding> binding; + private final T initialValue; + private final List> entries; + private final boolean collapsed; + private boolean available; + private final Class typeClass; + private final ImmutableSet flags; + private final EntryFactory entryFactory; + private final List>, List>> listeners; + private final List refreshListeners; + + public ListOptionImpl(@NotNull Text name, @NotNull Text tooltip, @NotNull Binding> binding, @NotNull T initialValue, @NotNull Class typeClass, @NotNull Function, Controller> controllerFunction, ImmutableSet flags, boolean collapsed, boolean available) { + this.name = name; + this.tooltip = tooltip; + this.binding = binding; + this.initialValue = initialValue; + this.entryFactory = new EntryFactory(controllerFunction); + this.entries = createEntries(binding().getValue()); + this.collapsed = collapsed; + this.typeClass = typeClass; + this.flags = flags; + this.available = available; + this.listeners = new ArrayList<>(); + this.refreshListeners = new ArrayList<>(); + callListeners(); + } + + @Override + public @NotNull Text name() { + return this.name; + } + + @Override + public @NotNull Text tooltip() { + return this.tooltip; + } + + @Override + public @NotNull ImmutableList> options() { + return ImmutableList.copyOf(entries); + } + + @Override + public @NotNull Controller> controller() { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull Binding> binding() { + return binding; + } + + @Override + public @NotNull Class> typeClass() { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull Class elementTypeClass() { + return typeClass; + } + + @Override + public boolean collapsed() { + return collapsed; + } + + @Override + public @NotNull ImmutableSet flags() { + return flags; + } + + @Override + public ImmutableList pendingValue() { + return ImmutableList.copyOf(entries.stream().map(Option::pendingValue).toList()); + } + + @Override + public void insertEntry(int index, ListOptionEntry entry) { + entries.add(index, (ListOptionEntry) entry); + onRefresh(); + } + + @Override + public ListOptionEntry insertNewEntryToTop() { + ListOptionEntry newEntry = entryFactory.create(initialValue); + entries.add(0, newEntry); + onRefresh(); + return newEntry; + } + + @Override + public void removeEntry(ListOptionEntry entry) { + entries.remove(entry); + onRefresh(); + } + + @Override + public int indexOf(ListOptionEntry entry) { + return entries.indexOf(entry); + } + + @Override + public void requestSet(List value) { + entries.clear(); + entries.addAll(createEntries(value)); + onRefresh(); + listeners.forEach(listener -> listener.accept(this, value)); + } + + @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) { + this.available = available; + } + + @Override + public void addListener(BiConsumer>, List> changedListener) { + this.listeners.add(changedListener); + } + + @Override + public void addRefreshListener(Runnable changedListener) { + this.refreshListeners.add(changedListener); + } + + @Override + public boolean isRoot() { + return false; + } + + private List> createEntries(Collection values) { + return values.stream().map(entryFactory::create).collect(Collectors.toList()); + } + + void callListeners() { + List pendingValue = pendingValue(); + this.listeners.forEach(listener -> listener.accept(this, pendingValue)); + } + + private void onRefresh() { + refreshListeners.forEach(Runnable::run); + callListeners(); + } + + private class EntryFactory { + private final Function, Controller> controllerFunction; + + private EntryFactory(Function, Controller> controllerFunction) { + this.controllerFunction = controllerFunction; + } + + public ListOptionEntry create(T initialValue) { + return new ListOptionEntryImpl<>(ListOptionImpl.this, initialValue, controllerFunction); + } + } +} diff --git a/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java b/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java index 58bc96b..02ef04c 100644 --- a/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/OptionGroupImpl.java @@ -6,5 +6,5 @@ import dev.isxander.yacl.api.OptionGroup; import net.minecraft.text.Text; import org.jetbrains.annotations.NotNull; -public record OptionGroupImpl(@NotNull Text name, @NotNull Text tooltip, ImmutableList> options, boolean collapsed, boolean isRoot) implements OptionGroup { +public record OptionGroupImpl(@NotNull Text name, @NotNull Text tooltip, ImmutableList> options, boolean collapsed, boolean isRoot) implements OptionGroup { } diff --git a/src/client/java/dev/isxander/yacl/impl/OptionImpl.java b/src/client/java/dev/isxander/yacl/impl/OptionImpl.java index 90158c7..0cc156f 100644 --- a/src/client/java/dev/isxander/yacl/impl/OptionImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/OptionImpl.java @@ -92,11 +92,6 @@ public class OptionImpl implements Option { return flags; } - @Override - public boolean requiresRestart() { - return flags.contains(OptionFlag.GAME_RESTART); - } - @Override public boolean changed() { return !binding().getValue().equals(pendingValue); diff --git a/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java b/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java index eb23eac..380929c 100644 --- a/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java +++ b/src/client/java/dev/isxander/yacl/impl/YetAnotherConfigLibImpl.java @@ -8,12 +8,77 @@ import dev.isxander.yacl.impl.utils.YACLConstants; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Text; +import java.util.Objects; import java.util.function.Consumer; -public record YetAnotherConfigLibImpl(Text title, ImmutableList categories, Runnable saveFunction, Consumer initConsumer) implements YetAnotherConfigLib { +public final class YetAnotherConfigLibImpl implements YetAnotherConfigLib { + private final Text title; + private final ImmutableList categories; + private final Runnable saveFunction; + private final Consumer initConsumer; + + private boolean generated = false; + + public YetAnotherConfigLibImpl(Text title, ImmutableList categories, Runnable saveFunction, Consumer 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 Text title() { + return title; + } + + @Override + public ImmutableList categories() { + return categories; + } + + @Override + public Runnable saveFunction() { + return saveFunction; + } + + @Override + public Consumer initConsumer() { + return initConsumer; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (YetAnotherConfigLibImpl) obj; + return Objects.equals(this.title, that.title) && + Objects.equals(this.categories, that.categories) && + Objects.equals(this.saveFunction, that.saveFunction) && + Objects.equals(this.initConsumer, that.initConsumer); + } + + @Override + public int hashCode() { + return Objects.hash(title, categories, saveFunction, initConsumer); + } + + @Override + public String toString() { + return "YetAnotherConfigLibImpl[" + + "title=" + title + ", " + + "categories=" + categories + ", " + + "saveFunction=" + saveFunction + ", " + + "initConsumer=" + initConsumer + ']'; + } + } -- cgit