diff --git a/src/main/java/brachy/modularui/drawable/text/DynamicComponent.java b/src/main/java/brachy/modularui/drawable/text/DynamicComponent.java index 8d6915f..e1aa7d1 100644 --- a/src/main/java/brachy/modularui/drawable/text/DynamicComponent.java +++ b/src/main/java/brachy/modularui/drawable/text/DynamicComponent.java @@ -5,6 +5,8 @@ import brachy.modularui.screen.viewport.GuiContext; import brachy.modularui.theme.WidgetTheme; +import brachy.modularui.widgets.TextWidget; + import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentContents; @@ -62,6 +64,11 @@ private Component getComp() { return getComp().getVisualOrderText(); } + @Override + public TextWidget asWidget() { + return new TextWidget<>(this::getComp); + } + @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { Component comp = getComp(); diff --git a/src/main/java/brachy/modularui/integration/emi/EmiRecipeViewerSlot.java b/src/main/java/brachy/modularui/integration/emi/EmiRecipeViewerSlot.java new file mode 100644 index 0000000..44076a1 --- /dev/null +++ b/src/main/java/brachy/modularui/integration/emi/EmiRecipeViewerSlot.java @@ -0,0 +1,162 @@ +package brachy.modularui.integration.emi; + +import brachy.modularui.drawable.ClientTooltipComponentIcon; +import brachy.modularui.drawable.GuiTextures; +import brachy.modularui.integration.recipeviewer.RecipeSlotRole; +import brachy.modularui.integration.recipeviewer.RecipeViewerSlotWidget; +import brachy.modularui.integration.recipeviewer.entry.EntryList; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidEntryList; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidStackList; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidTagList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemEntryList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemStackList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemTagList; + +import brachy.modularui.screen.viewport.ModularGuiContext; +import brachy.modularui.theme.WidgetThemeEntry; + +import dev.emi.emi.api.forge.ForgeEmiStack; +import dev.emi.emi.api.stack.EmiIngredient; +import dev.emi.emi.api.stack.EmiStack; +import dev.emi.emi.api.widget.SlotWidget; + +import dev.emi.emi.api.widget.TankWidget; + +import lombok.Getter; +import lombok.experimental.Accessors; + +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.UnaryOperator; + +public class EmiRecipeViewerSlot extends RecipeViewerSlotWidget { + + @ApiStatus.Internal + @Getter + private SlotWidget slotWidget; + private int x, y; + + @Accessors(fluent = true) + @Getter + private RecipeSlotRole recipeSlotRole; + private EntryList value; + private float chance = 1f; + + public EmiRecipeViewerSlot() { + super(); + slotWidget = new SlotWidget(EmiIngredient.of(Ingredient.EMPTY), 0, 0); + recipeSlotRole = RecipeSlotRole.RENDER_ONLY; + + size(18, 18); + + tooltipAutoUpdate(true); + tooltipDynamic(tooltip -> { + for (ClientTooltipComponent ctc : this.slotWidget.getTooltip(getContext().getAbsMouseX(), getContext().getAbsMouseY())) { + tooltip.addDrawableLine(new ClientTooltipComponentIcon(ctc)); + } + }); + } + + @Override + public EmiRecipeViewerSlot recipeSlotRole(RecipeSlotRole recipeSlotRole) { + this.recipeSlotRole = recipeSlotRole; + slotWidget.catalyst(recipeSlotRole == RecipeSlotRole.CATALYST); + return getThis(); + } + + @Override + public EmiRecipeViewerSlot value(FluidEntryList fluidEntryList) { + value = fluidEntryList; + rebuildEmiSlot(); + background(GuiTextures.SLOT_FLUID); + return getThis(); + } + + @Override + public EmiRecipeViewerSlot value(ItemEntryList itemEntryList) { + value = itemEntryList; + rebuildEmiSlot(); + background(GuiTextures.SLOT_ITEM); + return getThis(); + } + + @Override + public EmiRecipeViewerSlot chance(float chance) { + this.chance = chance; + return getThis(); + } + + private void rebuildEmiSlot() { + if (value instanceof ItemEntryList itemEntryList) { + slotWidget = new SlotWidget(EmiIngredientHandler.toEmiIngredient(itemEntryList, chance, UnaryOperator.identity()), 0, 0); + } else { + slotWidget = new TankWidget(EmiIngredientHandler.toEmiIngredient((FluidEntryList)value, chance), 0, 0, 18, 18, 1); + } + slotWidget.drawBack(false); + } + + @Override + public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { + context.getGraphics().pose().translate(-this.x, -this.y, 0); + this.slotWidget.render(context.getGraphics(), context.getMouseX(), context.getMouseY(), context.getRenderPartialTicks()); + context.getGraphics().pose().translate(this.x, this.y, 0); + } + + @Override + public Result onMousePressed(int button) { + this.slotWidget.mouseClicked(getContext().getMouseX(), getContext().getAbsMouseY(), button); + return Result.SUCCESS; + } + + @Override + public Result onKeyPressed(int keyCode, int scanCode, int modifiers) { + return this.slotWidget.keyPressed(keyCode, scanCode, modifiers) ? Result.SUCCESS : Result.ACCEPT; + } + + public static class EmiIngredientHandler { + + public static EmiIngredient toEmiIngredient(ItemEntryList list, float xeiChance, + UnaryOperator realStack) { + List ingredients = new ArrayList<>(); + if (list instanceof ItemTagList tagList) { + ingredients.addAll(tagList.getEntries().stream() + .map(ItemTagList.ItemTagEntry::stacks) + .map(stream -> EmiIngredient.of(stream.map(realStack).map(EmiStack::of).toList()).setChance(xeiChance)).toList()); + } + if (list instanceof ItemStackList stackList) { + ingredients.add(EmiIngredient.of(stackList.stream().map(realStack).map(EmiStack::of).toList()).setChance(xeiChance)); + } + + if (ingredients.isEmpty()) return EmiIngredient.of(Ingredient.EMPTY); + if (ingredients.size() == 1) return ingredients.get(0); + return EmiIngredient.of(ingredients); + } + + public static EmiIngredient toEmiIngredient(FluidEntryList list, float xeiChance) { + + List ingredients = new ArrayList<>(); + + if (list instanceof FluidTagList tagList) { + ingredients.addAll(tagList.getEntries().stream() + .map(FluidTagList.FluidTagEntry::stacks) + .map(stream -> EmiIngredient.of(stream.map(ForgeEmiStack::of).toList()).setChance(xeiChance)) + .toList() + ); + } + + if (list instanceof FluidStackList stackList) { + ingredients.add(EmiIngredient.of(stackList.stream().map(ForgeEmiStack::of).toList()).setChance(xeiChance)); + } + + if (ingredients.isEmpty()) return EmiIngredient.of(Ingredient.EMPTY); + if (ingredients.size() == 1) return ingredients.get(0); + return EmiIngredient.of(ingredients); + } + } +} diff --git a/src/main/java/brachy/modularui/integration/emi/recipe/EMISlotWidgetWrapper.java b/src/main/java/brachy/modularui/integration/emi/recipe/EMISlotWidgetWrapper.java deleted file mode 100644 index 2551bc0..0000000 --- a/src/main/java/brachy/modularui/integration/emi/recipe/EMISlotWidgetWrapper.java +++ /dev/null @@ -1,52 +0,0 @@ -package brachy.modularui.integration.emi.recipe; - -import brachy.modularui.api.widget.Interactable; -import brachy.modularui.drawable.ClientTooltipComponentIcon; -import brachy.modularui.screen.viewport.ModularGuiContext; -import brachy.modularui.theme.WidgetThemeEntry; -import brachy.modularui.widget.Widget; - -import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; - -import dev.emi.emi.api.widget.Bounds; -import dev.emi.emi.api.widget.SlotWidget; - -public class EMISlotWidgetWrapper> extends Widget implements Interactable { - - private final SlotWidget slotWidget; - private final int x, y; - - public EMISlotWidgetWrapper(SlotWidget slotWidget) { - this.slotWidget = slotWidget; - Bounds bounds = this.slotWidget.getBounds(); - size(bounds.width(), bounds.height()); - this.x = bounds.x(); - this.y = bounds.y(); - - tooltipAutoUpdate(true); - tooltipDynamic(tooltip -> { - for (ClientTooltipComponent ctc : this.slotWidget.getTooltip(getContext().getAbsMouseX(), getContext().getAbsMouseY())) { - tooltip.addDrawableLine(new ClientTooltipComponentIcon(ctc)); - } - }); - } - - @Override - public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { - super.draw(context, widgetTheme); - context.getGraphics().pose().translate(-this.x, -this.y, 0); - this.slotWidget.render(context.getGraphics(), context.getMouseX(), context.getMouseY(), context.getRenderPartialTicks()); - context.getGraphics().pose().translate(this.x, this.y, 0); - } - - @Override - public Result onMousePressed(int button) { - this.slotWidget.mouseClicked(getContext().getMouseX(), getContext().getAbsMouseY(), button); - return Result.SUCCESS; - } - - @Override - public Result onKeyPressed(int keyCode, int scanCode, int modifiers) { - return this.slotWidget.keyPressed(keyCode, scanCode, modifiers) ? Result.SUCCESS : Result.ACCEPT; - } -} diff --git a/src/main/java/brachy/modularui/integration/emi/recipe/ModularUIEmiRecipe.java b/src/main/java/brachy/modularui/integration/emi/recipe/ModularUIEmiRecipe.java index 544ae51..4166c94 100644 --- a/src/main/java/brachy/modularui/integration/emi/recipe/ModularUIEmiRecipe.java +++ b/src/main/java/brachy/modularui/integration/emi/recipe/ModularUIEmiRecipe.java @@ -1,19 +1,16 @@ package brachy.modularui.integration.emi.recipe; import brachy.modularui.ModularUI; -import brachy.modularui.api.IThemeApi; import brachy.modularui.api.drawable.IRichTextBuilder; import brachy.modularui.api.widget.ITooltip; import brachy.modularui.api.widget.IWidget; import brachy.modularui.drawable.text.RichText; +import brachy.modularui.integration.emi.EmiRecipeViewerSlot; import brachy.modularui.integration.recipeviewer.RecipeSlotRole; -import brachy.modularui.integration.recipeviewer.handlers.IngredientProvider; import brachy.modularui.screen.EmbedHandler; import brachy.modularui.screen.ModularPanel; import brachy.modularui.screen.ModularScreen; import brachy.modularui.screen.RichTooltip; -import brachy.modularui.theme.WidgetThemeKey; -import brachy.modularui.widgets.slot.FluidSlot; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; @@ -27,7 +24,6 @@ import dev.emi.emi.api.stack.EmiStack; import dev.emi.emi.api.widget.Bounds; import dev.emi.emi.api.widget.SlotWidget; -import dev.emi.emi.api.widget.TankWidget; import dev.emi.emi.api.widget.Widget; import dev.emi.emi.api.widget.WidgetHolder; import org.jetbrains.annotations.Nullable; @@ -118,41 +114,14 @@ public ModularPanel transform(ModularPanel panel) { } public IWidget transformWidget(IWidget widget, Iterator in, Iterator out) { - if (!(widget instanceof IngredientProvider provider)) return widget; - RecipeSlotRole role = provider.getRecipeRole(); - if (role == RecipeSlotRole.RENDER_ONLY) return widget; + if (!(widget instanceof EmiRecipeViewerSlot recipeViewerSlot)) return widget; - EmiIngredient ingr = EmiStack.EMPTY; - if (role == RecipeSlotRole.INPUT && in.hasNext()) { - ingr = in.next(); - } else if (role == RecipeSlotRole.OUTPUT && out.hasNext()) { - ingr = out.next(); + if (recipeViewerSlot.recipeSlotRole() == RecipeSlotRole.OUTPUT) { + recipeViewerSlot.getSlotWidget().recipeContext(this); } - SlotWidget emiSlot = null; - WidgetThemeKey widgetThemeKey = null; - if (widget instanceof FluidSlot slot) { - if (!slot.isAlwaysShowFull()) { - emiSlot = new TankWidget(ingr, 0, 0, slot.getArea().width, slot.getArea().height, slot.getCapacity()); - } - widgetThemeKey = IThemeApi.FLUID_SLOT; - } - if (emiSlot == null) emiSlot = new SlotWidget(ingr, 0, 0); - if (widgetThemeKey == null) widgetThemeKey = IThemeApi.ITEM_SLOT; - - emiSlot.drawBack(false); - - if (role == RecipeSlotRole.CATALYST) { - emiSlot.catalyst(true); - } else if (role == RecipeSlotRole.OUTPUT) { - emiSlot.recipeContext(this); - } - - return new EMISlotWidgetWrapper<>(emiSlot) - .copyResizerOf(widget) - .copyVisualsOf(widget) - .widgetTheme(widgetThemeKey); + return recipeViewerSlot; } @Override diff --git a/src/main/java/brachy/modularui/integration/jei/JeiRecipeViewerSlot.java b/src/main/java/brachy/modularui/integration/jei/JeiRecipeViewerSlot.java new file mode 100644 index 0000000..66598ab --- /dev/null +++ b/src/main/java/brachy/modularui/integration/jei/JeiRecipeViewerSlot.java @@ -0,0 +1,82 @@ +package brachy.modularui.integration.jei; + +import brachy.modularui.integration.recipeviewer.RecipeViewerSlotWidget; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidEntryList; + +import brachy.modularui.integration.recipeviewer.entry.item.ItemEntryList; + +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.runtime.IIngredientManager; +import mezz.jei.common.input.ClickableIngredient; + +import mezz.jei.common.util.ImmutableRect2i; + +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import static brachy.modularui.integration.jei.ModularUIJeiPlugin.jeiHelpers; + +public class JeiRecipeViewerSlot { + + public static class JeiIngredientHandler { + + public static List toJeiIngredient(FluidEntryList list) { + return list.getStacks() + .stream() + .filter(stack -> !stack.isEmpty()) + .map(JeiIngredientHandler::getJEIFluid) + .toList(); + } + + public static List toJeiIngredientClickable(FluidEntryList list, int x, int y, int w, int h) { + return list.getStacks() + .stream() + .filter(stack -> !stack.isEmpty()) + .map(stack -> getJEIFluidClickable(stack, x, y, w, h)) + .toList(); + } + + public static Object getJEIFluid(FluidStack fluidStack) { + return jeiHelpers.getPlatformFluidHelper().create(fluidStack.getFluid(), fluidStack.getAmount(), fluidStack.getTag()); + } + + public static @Nullable Object getJEIFluidClickable(FluidStack fluidStack, int x, int y, int w, int h) { + var ingredient = jeiHelpers.getPlatformFluidHelper().create(fluidStack.getFluid(), fluidStack.getAmount(), fluidStack.getTag()); + return jeiHelpers.getIngredientManager().createTypedIngredient(ingredient) + .map(typedIngredient -> new ClickableIngredient<>(typedIngredient, new ImmutableRect2i(x, y, w, h))) + .orElse(null); + } + + public static @Nullable Object getJEIStackClickable(ItemStack stack, int x, int y, int w, int h) { + IIngredientManager ingredientManager = jeiHelpers.getIngredientManager(); + return ingredientManager.createTypedIngredient(VanillaTypes.ITEM_STACK, stack) + .map(typedIngredient -> new ClickableIngredient<>(typedIngredient, new ImmutableRect2i(x, y, w, h))) + .orElse(null); + } + + public static List toJeiIngredient(ItemEntryList list, UnaryOperator realStack) { + return list.getStacks() + .stream() + .filter(stack -> !stack.isEmpty()) + .map(realStack) + .collect(Collectors.toList()); + } + + public static List toJeiIngredientClickable(ItemEntryList list, int x, int y, int w, int h, + UnaryOperator realStack) { + return list.getStacks() + .stream() + .filter(stack -> !stack.isEmpty()) + .map(realStack) + .map(stack -> getJEIStackClickable(stack, x, y, w, h)) + .collect(Collectors.toList()); + } + + } +} diff --git a/src/main/java/brachy/modularui/integration/jei/ModularUIJeiPlugin.java b/src/main/java/brachy/modularui/integration/jei/ModularUIJeiPlugin.java index 7a1f4dd..868aca7 100644 --- a/src/main/java/brachy/modularui/integration/jei/ModularUIJeiPlugin.java +++ b/src/main/java/brachy/modularui/integration/jei/ModularUIJeiPlugin.java @@ -7,6 +7,10 @@ import brachy.modularui.screen.ModularContainerMenu; import brachy.modularui.screen.ScreenWrapper; +import mezz.jei.api.helpers.IJeiHelpers; + +import mezz.jei.api.registration.IRecipeCategoryRegistration; + import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.resources.ResourceLocation; @@ -26,6 +30,7 @@ public class ModularUIJeiPlugin implements IModPlugin { @Getter private static IJeiRuntime runtime = null; + public static IJeiHelpers jeiHelpers; public static boolean hasRuntime() { return runtime != null; @@ -41,6 +46,11 @@ public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { runtime = jeiRuntime; } + @Override + public void registerCategories(IRecipeCategoryRegistration registration) { + jeiHelpers = registration.getJeiHelpers(); + } + @Override public void registerGuiHandlers(IGuiHandlerRegistration registration) { if (ModularUI.Mods.REI.isLoaded() || ModularUI.Mods.EMI.isLoaded()) return; diff --git a/src/main/java/brachy/modularui/integration/recipeviewer/RecipeViewerSlotWidget.java b/src/main/java/brachy/modularui/integration/recipeviewer/RecipeViewerSlotWidget.java new file mode 100644 index 0000000..bf4a181 --- /dev/null +++ b/src/main/java/brachy/modularui/integration/recipeviewer/RecipeViewerSlotWidget.java @@ -0,0 +1,46 @@ +package brachy.modularui.integration.recipeviewer; + +import brachy.modularui.ModularUI; +import brachy.modularui.api.widget.Interactable; +import brachy.modularui.integration.emi.EmiRecipeViewerSlot; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidEntryList; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidStackList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemEntryList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemStackList; +import brachy.modularui.widget.Widget; + +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import org.apache.commons.lang3.NotImplementedException; + +public abstract class RecipeViewerSlotWidget> extends Widget implements Interactable { + + public abstract T recipeSlotRole(RecipeSlotRole recipeSlotRole); + + public abstract T value(FluidEntryList fluidEntryList); + public abstract T value(ItemEntryList itemEntryList); + + public T value(ItemStack stack) { + return value(ItemStackList.of(stack)); + } + + public T value(FluidStack stack) { + return value(FluidStackList.of(stack)); + } + + public abstract T chance(float chance); + + public static RecipeViewerSlotWidget create() { + if (!ModularUI.Mods.isRecipeViewerLoaded()) throw new IllegalStateException("Cannot create recipe viewer slot without a recipe viewer mod loaded."); + + if (ModularUI.Mods.EMI.isLoaded()) { + return new EmiRecipeViewerSlot(); + } //else if (ModularUI.Mods.JEI.isLoaded()) { + // return new JeiRecipeViewerSlot(); + //}/else { + // return new ReiRecipeViewerSlot(); + //} + throw new NotImplementedException(); + } +} diff --git a/src/main/java/brachy/modularui/integration/rei/ReiRecipeViewerSlot.java b/src/main/java/brachy/modularui/integration/rei/ReiRecipeViewerSlot.java new file mode 100644 index 0000000..462c499 --- /dev/null +++ b/src/main/java/brachy/modularui/integration/rei/ReiRecipeViewerSlot.java @@ -0,0 +1,69 @@ +package brachy.modularui.integration.rei; + +import brachy.modularui.integration.recipeviewer.RecipeViewerSlotWidget; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidEntryList; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidStackList; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidTagList; + +import brachy.modularui.integration.recipeviewer.entry.item.ItemEntryList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemStackList; +import brachy.modularui.integration.recipeviewer.entry.item.ItemTagList; + +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.util.EntryStacks; + +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import java.util.Collections; +import java.util.List; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ReiRecipeViewerSlot { + + public static class ReiIngredientHandler { + + private static EntryIngredient toReiIngredient(Stream stream) { + return EntryIngredient.of(stream + .map(s -> dev.architectury.fluid.FluidStack.create(s.getFluid(), s.getAmount(), s.getTag())) + .map(EntryStacks::of) + .toList()); + } + + private static EntryIngredient toReiIngredient(Stream stream, UnaryOperator realStack) { + return EntryIngredient.of(stream.map(realStack) + .map(EntryStacks::of) + .toList()); + } + + public static List toReiIngredient(FluidEntryList list) { + if (list instanceof FluidTagList tagList) { + return tagList.getEntries().stream() + .map(FluidTagList.FluidTagEntry::stacks) + .map(ReiIngredientHandler::toReiIngredient) + .collect(Collectors.toList()); + } + + if (list instanceof FluidStackList stackList) { + return List.of(toReiIngredient(stackList.stream())); + } + return Collections.emptyList(); + } + + public static List toReiIngredient(ItemEntryList list, UnaryOperator realStack) { + if (list instanceof ItemTagList tagList) { + return tagList.getEntries().stream() + .map(ItemTagList.ItemTagEntry::stacks) + .map(stream -> toReiIngredient(stream, realStack)) + .collect(Collectors.toList()); + + } + if (list instanceof ItemStackList stackList) { + return List.of(toReiIngredient(stackList.stream(), realStack)); + } + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/brachy/modularui/test/TestMachine.java b/src/main/java/brachy/modularui/test/TestMachine.java index 8bfa1a9..e787d29 100644 --- a/src/main/java/brachy/modularui/test/TestMachine.java +++ b/src/main/java/brachy/modularui/test/TestMachine.java @@ -10,6 +10,8 @@ import brachy.modularui.factory.PosGuiData; import brachy.modularui.integration.emi.recipe.ModularUIEmiRecipe; import brachy.modularui.integration.recipeviewer.RecipeSlotRole; +import brachy.modularui.integration.recipeviewer.RecipeViewerSlotWidget; +import brachy.modularui.integration.recipeviewer.entry.fluid.FluidStackList; import brachy.modularui.screen.ModularPanel; import brachy.modularui.screen.ModularScreen; import brachy.modularui.screen.UISettings; @@ -42,9 +44,11 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluids; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemHandlerHelper; @@ -285,6 +289,30 @@ public static IWidget buildMachineUI(IItemHandler in, IItemHandler out, IDoubleV .slot(new ModularSlot(out, i).canPut(false)) .recipeRole(RecipeSlotRole.OUTPUT))); } + + public static IWidget buildViewerUI(Recipe recipe) { + return Flow.row().name("slots") + .center() + .childPadding(8) + .child(SlotGroupWidget.rect(2, 2, i -> { + var in = i >= recipe.in.size() ? ItemStack.EMPTY : recipe.in.get(i); + if (in == null) in = ItemStack.EMPTY; + return RecipeViewerSlotWidget.create() + .recipeSlotRole(RecipeSlotRole.INPUT) + .value(in); + })) + .child(new ProgressWidget() + .value(DoubleValue.simulateProgress(5000)) + .size(20) + .texture(GuiTextures.PROGRESS_ARROW, 20)) + .child(SlotGroupWidget.rect(2, 2, i -> { + var out = i >= recipe.out.size() ? ItemStack.EMPTY : recipe.out.get(i); + if (out == null) out = ItemStack.EMPTY; + return RecipeViewerSlotWidget.create() + .recipeSlotRole(RecipeSlotRole.OUTPUT) + .value(out); + })); + } } public static class EMI { @@ -294,7 +322,7 @@ public static class EMI { public static void register(EmiRegistry registry) { registry.addCategory(CATEGORY); Recipes.list.stream() - .map(r -> new RecipeDisplay(() -> Recipes.buildMachineUI(EMPTY_INFINITE_ITEM_HANDLER, EMPTY_INFINITE_ITEM_HANDLER, DoubleValue.simulateProgress(5000)), r)) + .map(r -> new RecipeDisplay(() -> Recipes.buildViewerUI(r), r)) .forEach(registry::addRecipe); } diff --git a/src/main/java/brachy/modularui/widgets/ScrollingTextWidget.java b/src/main/java/brachy/modularui/widgets/ScrollingTextWidget.java index 1408abf..b32929a 100755 --- a/src/main/java/brachy/modularui/widgets/ScrollingTextWidget.java +++ b/src/main/java/brachy/modularui/widgets/ScrollingTextWidget.java @@ -62,9 +62,9 @@ public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { animator(new Animator().curve(Interpolation.SINE_INOUT)); } if (this.line == null) { - updateLine(getKey()); + updateLine(checkComponentUpdated()); } - checkString(); + checkComponentUpdated(); WidgetTheme theme = getActiveWidgetTheme(widgetTheme, isHovering()); TextRenderer renderer = TextRenderer.SHARED; renderer.setColor(getColor() != null ? getColor().getAsInt() : theme.getTextColor()); diff --git a/src/main/java/brachy/modularui/widgets/TextWidget.java b/src/main/java/brachy/modularui/widgets/TextWidget.java index 2d22d5a..660d03c 100644 --- a/src/main/java/brachy/modularui/widgets/TextWidget.java +++ b/src/main/java/brachy/modularui/widgets/TextWidget.java @@ -14,14 +14,16 @@ import net.minecraft.network.chat.Component; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Objects; import java.util.function.IntSupplier; +import java.util.function.Supplier; public class TextWidget> extends Widget { - @Getter private final Component key; + @Getter private Component key; @Getter private Alignment alignment = Alignment.CenterLeft; @Getter private IntSupplier color = null; @Getter private Boolean textShadow = null; @@ -29,9 +31,16 @@ public class TextWidget> extends Widget { @Getter private int maxWidth = -1; private String lastText; + private @Nullable Supplier keySupplier; + + public TextWidget(@NotNull Supplier keySupplier) { + this.keySupplier = keySupplier; + key = keySupplier.get(); + } public TextWidget(Component key) { this.key = key; + keySupplier = null; } public TextWidget(String key) { @@ -41,7 +50,7 @@ public TextWidget(String key) { @Override public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { TextRenderer renderer = TextRenderer.SHARED; - Component text = checkString(); + Component text = checkComponentUpdated(); WidgetTheme theme = getActiveWidgetTheme(widgetTheme, isHovering()); renderer.setColor(this.color != null ? this.color.getAsInt() : theme.getTextColor()); renderer.setAlignment(this.alignment, getArea().paddedWidth() + this.scale, getArea().paddedHeight()); @@ -52,13 +61,33 @@ public void draw(ModularGuiContext context, WidgetThemeEntry widgetTheme) { renderer.draw(context.getGraphics(), text); } - protected Component checkString() { - String text = this.key.getString(); - if (!Objects.equals(this.lastText, text)) { - onTextChanged(this.key); - this.lastText = text; + public W value(Supplier textSupplier) { + keySupplier = textSupplier; + return getThis(); + } + + public W value(Component text) { + key = text; + keySupplier = null; + return getThis(); + } + + public W value(String str) { + return value(Text.str(str)); + } + + protected Component checkComponentUpdated() { + if (keySupplier != null) { + var newKey = keySupplier.get(); + if (!Objects.equals(newKey, key)) { + key = newKey; + } + } + if (!Objects.equals(lastText, key.getString())) { + onTextChanged(key); + this.lastText = key.getString(); } - return this.key; + return key; } protected void onTextChanged(Component newText) { @@ -73,7 +102,8 @@ private TextRenderer simulate(float maxWidth) { renderer.setPos(padding.left(), padding.top()); renderer.setScale(this.scale); renderer.setSimulate(true); - renderer.draw(null, this.key); + // Don't update the key here, otherwise an infinite loop of checkComponentUpdated -> simulate -> checkComponentUpdated occurs + renderer.draw(null, key); renderer.setSimulate(false); return renderer; }