You have no choice

This commit is contained in:
skippyall
2026-05-02 23:33:45 +02:00
parent f3c934c619
commit bd87a15cf2
7 changed files with 145 additions and 183 deletions
@@ -8,7 +8,9 @@ import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.Items;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public class PaginatedList extends MinionsGui {
private int page = 0;
@@ -16,6 +18,7 @@ public class PaginatedList extends MinionsGui {
private final Component title;
private final int size;
private final BiFunction<Integer, PaginatedList, GuiElementBuilder> display;
private Runnable onClose = null;
public PaginatedList(MinionsGui parent, Component title, int size, BiFunction<Integer, PaginatedList, GuiElementBuilder> display) {
super(parent);
@@ -25,12 +28,20 @@ public class PaginatedList extends MinionsGui {
open();
}
public PaginatedList(MinionsGui parent, Component title, int size, BiFunction<Integer, PaginatedList, GuiElementBuilder> display, Runnable onClose) {
this(parent, title, size, display);
this.onClose = onClose;
}
@Override
protected void open() {
gui = new SimpleGui(MenuType.GENERIC_9x6, viewer, false) {
@Override
public void onPlayerClose(boolean success) {
onBackingClosed();
if(onClose != null) {
onClose.run();
}
}
};
gui.setTitle(title);
@@ -53,6 +64,17 @@ public class PaginatedList extends MinionsGui {
new PaginatedList(parent, title, list.size(), (i, gui) -> display.apply(list.byId(i), gui));
}
public static <T> CompletableFuture<T> createListFuture(MinionsGui parent, Component title, List<T> list, Function<T, GuiElementBuilder> display) {
CompletableFuture<T> future = new CompletableFuture<>();
new PaginatedList(parent, title, list.size(), (i, me) -> display.apply(list.get(i))
.setCallback(() -> {
future.complete(list.get(i));
me.goBack();
})
);
return future;
}
private void addItems() {
int slot = 9;
for(int i = 36 * page; i < Math.min(36 * (page + 1), size); i++) {
@@ -0,0 +1,71 @@
package io.github.skippyall.minions.gui.input
import eu.pb4.sgui.api.elements.GuiElementBuilder
import eu.pb4.sgui.api.gui.SimpleGui
import io.github.skippyall.minions.gui.MinionsGui
import io.github.skippyall.minions.gui.minion.SimpleMinionsGui
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.future.asCompletableFuture
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.Items
import java.util.concurrent.CompletableFuture
object BooleanInput {
fun confirm(
parent: MinionsGui,
title: Component,
falseText: Component = Component.translatable("minions.gui.abort"),
trueText: Component = Component.translatable("minions.gui.confirm")
): CompletableDeferred<Boolean> {
val deferred = CompletableDeferred<Boolean>()
SimpleMinionsGui(parent) { onClose: Runnable, me: SimpleMinionsGui ->
val gui: SimpleGui = object : SimpleGui(MenuType.GENERIC_3x3, parent.viewer, false) {
override fun onPlayerClose(success: Boolean) {
deferred.complete(false)
onClose.run()
}
}
gui.setTitle(title)
gui.setSlot(
3, GuiElementBuilder(Items.REDSTONE_BLOCK)
.setName(falseText)
.setCallback(Runnable {
deferred.complete(false)
me.goBack()
})
)
gui.setSlot(
5, GuiElementBuilder(Items.EMERALD_BLOCK)
.setName(trueText)
.setCallback(Runnable {
deferred.complete(true)
me.goBack()
})
)
gui.open()
gui
}
return deferred
}
@JvmStatic
@JvmOverloads
fun confirmFuture(
parent: MinionsGui,
title: Component,
falseText: Component = Component.translatable("minions.gui.abort"),
trueText: Component = Component.translatable("minions.gui.confirm")
): CompletableFuture<Boolean> {
return confirm(
parent,
title,
falseText,
trueText
).asCompletableFuture()
}
}
@@ -1,122 +0,0 @@
package io.github.skippyall.minions.gui.input;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.gui.Displayable;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.gui.MinionsGui;
import io.github.skippyall.minions.gui.minion.SimpleMinionsGui;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
public class ChoiceInput {
public static <T> BiFunction<ServerPlayer, T, CompletableFuture<T>> createDialogOpener(MenuType<?> screen, Component title, Function<T, GuiDisplay> displayFunction, T[] values, @Nullable T fallback) {
return (player, object) -> {
CompletableFuture<T> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(screen, player, false) {
@Override
public void onPlayerClose(boolean success) {
if(fallback == null) {
future.cancel(false);
} else {
future.complete(fallback);
}
}
};
gui.setTitle(title);
for(T value : values) {
gui.addSlot(new GuiElementBuilder(displayFunction.apply(value).createItemStack())
.setCallback(() -> future.complete(value))
);
}
gui.open();
return future;
};
}
public static <T extends Displayable> BiFunction<ServerPlayer, T, CompletableFuture<T>> createDialogOpener(T[] values) {
return createDialogOpener(MenuType.GENERIC_9x3, Component.empty(), t -> t != null ? t.getDisplay() : null, values, null);
}
public static CompletableFuture<Void> confirm(ServerPlayer player, Component title) {
CompletableFuture<Void> future = new CompletableFuture<>();
SimpleGui gui = new SimpleGui(MenuType.GENERIC_3x3, player, false) {
@Override
public void onPlayerClose(boolean success) {
future.cancel(false);
}
};
gui.setTitle(title);
gui.setSlot(3, new GuiElementBuilder(Items.REDSTONE_BLOCK)
.setName(Component.translatable("minions.gui.abort"))
.setCallback(() -> future.cancel(false))
);
gui.setSlot(5, new GuiElementBuilder(Items.EMERALD_BLOCK)
.setName(Component.translatable("minions.gui.confirm"))
.setCallback(() -> future.complete(null))
);
gui.open();
return future;
}
public static CompletableFuture<Boolean> confirm(MinionsGui parent, Component title) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
new SimpleMinionsGui(parent, (onClose, me) -> {
SimpleGui gui = new SimpleGui(MenuType.GENERIC_3x3, parent.viewer, false) {
@Override
public void onPlayerClose(boolean success) {
future.complete(false);
onClose.run();
}
};
gui.setTitle(title);
gui.setSlot(3, new GuiElementBuilder(Items.REDSTONE_BLOCK)
.setName(Component.translatable("minions.gui.abort"))
.setCallback(() -> {
future.complete(false);
me.goBack();
})
);
gui.setSlot(5, new GuiElementBuilder(Items.EMERALD_BLOCK)
.setName(Component.translatable("minions.gui.confirm"))
.setCallback(() -> {
future.complete(true);
me.goBack();
})
);
gui.open();
return gui;
});
return future;
}
public static BiFunction<ServerPlayer, Boolean, CompletableFuture<Boolean>> inputBoolean(Component title) {
return createDialogOpener(MenuType.GENERIC_3x3, title, value -> {
if(value) {
return new GuiDisplay.ItemBased(Items.EMERALD_BLOCK);
} else {
return new GuiDisplay.ItemBased(Items.REDSTONE_BLOCK);
}
}, new Boolean[]{false, true}, false);
}
}
@@ -3,8 +3,8 @@ package io.github.skippyall.minions.gui.input
import eu.pb4.sgui.api.elements.GuiElementBuilder
import eu.pb4.sgui.api.gui.AnvilInputGui
import io.github.skippyall.minions.gui.MinionsGui
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import net.minecraft.network.chat.Component
@@ -29,9 +29,7 @@ class TextInput<T>(
private lateinit var gui: AnvilInputGui
private var result: Result<T, Component>? = null
private var isConfirm = false
val job = Job()
val deferred = CompletableDeferred<T?>()
init {
updateConfirmButton(defaultValue)
@@ -46,8 +44,8 @@ class TextInput<T>(
override fun onPlayerClose(success: Boolean) {
onBackingClosed()
if (job.isActive && !isConfirm) {
job.cancel()
if (deferred.isActive) {
deferred.complete(null)
}
}
}
@@ -75,20 +73,19 @@ class TextInput<T>(
}
fun onConfirm() {
result?.ifSuccess { _: T? ->
isConfirm = true
result?.ifSuccess { success: T ->
deferred.complete(success)
}
job.complete()
}
companion object {
@JvmStatic
suspend fun <T>input(
fun <T>input(
gui: MinionsGui,
title: Component,
defaultValue: String,
parser: suspend (String) -> Result<T, Component>,
): T? {
): Deferred<T?> {
val input = TextInput(
parent = gui,
title = title,
@@ -96,9 +93,7 @@ class TextInput<T>(
parser = parser,
)
input.job.join()
return input.result?.getOrDefault(null)
return input.deferred
}
@JvmStatic
@@ -108,26 +103,20 @@ class TextInput<T>(
defaultValue: String,
parser: (String) -> Result<T, Component>,
): CompletableFuture<T?> {
return gui.scope.async {
val input = TextInput(
parent = gui,
title = title,
defaultValue = defaultValue,
parser = parser,
)
input.job.join()
return@async input.result?.getOrDefault(null)
}.asCompletableFuture()
return input(
gui = gui,
title = title,
defaultValue = defaultValue,
parser = parser
).asCompletableFuture()
}
@JvmStatic
suspend fun inputString(
fun inputString(
gui: MinionsGui,
title: Component,
defaultValue: String,
): String? {
): Deferred<String?> {
return input<String>(
gui = gui,
title = title,
@@ -142,21 +131,19 @@ class TextInput<T>(
title: Component,
defaultValue: String,
): CompletableFuture<String?> {
return gui.scope.async {
inputString(
gui,
title,
defaultValue
)
}.asCompletableFuture()
return inputString(
gui = gui,
title = title,
defaultValue = defaultValue,
).asCompletableFuture()
}
@JvmStatic
suspend fun inputLong(
fun inputLong(
gui: MinionsGui,
title: Component,
defaultValue: Long,
): Long? {
): Deferred<Long?> {
return input<Long>(
gui = gui,
title = title,
@@ -176,21 +163,19 @@ class TextInput<T>(
title: Component,
defaultValue: Long,
): CompletableFuture<Long?> {
return gui.scope.async {
inputLong(
gui,
title,
defaultValue
)
}.asCompletableFuture()
return inputLong(
gui = gui,
title = title,
defaultValue = defaultValue,
).asCompletableFuture()
}
@JvmStatic
suspend fun inputDouble(
fun inputDouble(
gui: MinionsGui,
title: Component,
defaultValue: Double,
): Double? {
): Deferred<Double?> {
return input<Double>(
gui = gui,
title = title,
@@ -210,13 +195,11 @@ class TextInput<T>(
title: Component,
defaultValue: Double,
): CompletableFuture<Double?> {
return gui.scope.async {
inputDouble(
gui,
title,
defaultValue
)
}.asCompletableFuture()
return inputDouble(
gui = gui,
title = title,
defaultValue = defaultValue,
).asCompletableFuture()
}
}
}
@@ -4,7 +4,7 @@ import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.clipboard.ClipboardItem;
import io.github.skippyall.minions.gui.MinionsGui;
import io.github.skippyall.minions.gui.input.ChoiceInput;
import io.github.skippyall.minions.gui.input.BooleanInput;
import io.github.skippyall.minions.gui.minion.GuiContext;
import io.github.skippyall.minions.minion.MinionListener;
import io.github.skippyall.minions.minion.MinionRuntime;
@@ -60,7 +60,7 @@ public class ConfigureInstructionGui extends MinionsGui implements ConfiguredIns
gui.setSlot(7, new GuiElementBuilder(Items.LAVA_BUCKET)
.setName(Component.translatable("minions.gui.instruction.configure.delete"))
.setCallback(() -> ChoiceInput.confirm(this, Component.translatable("minions.gui.instruction.configure.delete.confirm", name))
.setCallback(() -> BooleanInput.confirmFuture(this, Component.translatable("minions.gui.instruction.configure.delete.confirm", name))
.thenAccept((confirmed) -> {
if(confirmed) {
minion.getInstructionManager().removeInstruction(name);
@@ -1,7 +1,10 @@
package io.github.skippyall.minions.registration;
import com.mojang.serialization.Codec;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.gui.PaginatedList;
import io.github.skippyall.minions.gui.input.BooleanInput;
import io.github.skippyall.minions.gui.input.TextInput;
import io.github.skippyall.minions.minion.program.instruction.move.TurnDirection;
import io.github.skippyall.minions.program.value.SimpleValueType;
@@ -10,7 +13,7 @@ import net.minecraft.core.Registry;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
import java.util.concurrent.CompletableFuture;
import java.util.List;
public class ValueTypes {
public static ValueType<Long> LONG = register(
@@ -49,8 +52,7 @@ public class ValueTypes {
Codec.BOOL,
false,
o -> o instanceof Boolean b ? b : null,
//TODO Properly implement ChoiceInput
(gui, value) -> CompletableFuture.completedFuture(value),//ChoiceInput.inputBoolean(Text.literal("")),
(parent, value) -> BooleanInput.confirmFuture(parent, Component.literal(""), Component.translatable("value_type.minions.boolean.false"), Component.translatable("value_type.minions.boolean.true")),
value -> Component.literal(value.toString())
)
);
@@ -76,8 +78,12 @@ public class ValueTypes {
TurnDirection.CODEC,
TurnDirection.RIGHT,
o -> o instanceof TurnDirection d ? d : null,
//TODO Properly implement ChoiceInput
(parent, oldValue) -> CompletableFuture.completedFuture(oldValue), // ChoiceInput.createDialogOpener(TurnDirection.values()),
(parent, oldValue) -> PaginatedList.createListFuture(
parent,
Component.translatable("value_type.minions.turn_direction"),
List.of(TurnDirection.values()),
value -> new GuiElementBuilder(value.getDisplay().createItemStack())
),
value -> Component.literal(value.name)
)
);