Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,31 +1,52 @@
package brachy.modularui.integration.jei;

import brachy.modularui.api.drawable.IDrawable;
import brachy.modularui.api.widget.IWidget;
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.widget.Widget;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import mezz.jei.api.gui.builder.IRecipeSlotBuilder;
import mezz.jei.api.gui.ingredient.IRecipeSlotDrawable;

import net.minecraftforge.fluids.FluidStack;

import org.apache.commons.lang3.NotImplementedException;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unused imports & apply other formatting cleanups please

import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Experimental
public class JeiRecipeViewerSlot extends RecipeViewerSlotWidget<JeiRecipeViewerSlot> {
@Accessors(fluent = true)
@Getter
@Setter
private RecipeSlotRole recipeSlotRole;
@Getter
private EntryList<?> value;
@Accessors(fluent = true)
@Getter
@Setter
private float chance = 1f;

public JeiRecipeViewerSlot() {
throw new NotImplementedException();
}
recipeSlotRole = RecipeSlotRole.RENDER_ONLY;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this assigned somewhere else, or?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the field has @Setter above it

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh righto
in that case, could you move the assignment to the field definition?


@Override
public JeiRecipeViewerSlot recipeSlotRole(RecipeSlotRole recipeSlotRole) {
return getThis();
size(18, 18);
}

@Override
public <T> JeiRecipeViewerSlot value(EntryList<T> entryList) {
return getThis();
}

@Override
public JeiRecipeViewerSlot chance(float chance) {
this.value = entryList;
if (this.value.getType() == FluidStack.class) {
background(GuiTextures.SLOT_FLUID);
} else {
background(GuiTextures.SLOT_ITEM); // TODO other types
}
return getThis();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package brachy.modularui.integration.jei.recipe;

import brachy.modularui.api.widget.IWidget;
import brachy.modularui.integration.jei.JeiRecipeViewerSlot;
import brachy.modularui.integration.jei.ModularUIJeiPlugin;
import brachy.modularui.integration.recipeviewer.RecipeSlotRole;
import brachy.modularui.integration.recipeviewer.entry.EntryList;
import brachy.modularui.integration.recipeviewer.handlers.IngredientProvider;
import brachy.modularui.integration.recipeviewer.util.RecipeScreenRenderingUtil;
import brachy.modularui.screen.ModularPanel;
Expand Down Expand Up @@ -68,18 +70,18 @@ private ModularScreen getModularScreen(T recipe) {
return this.modularScreenCache.getUnchecked(recipe);
}

private static <T> void addJEISlot(IRecipeLayoutBuilder builder, IngredientProvider<T> widget,
private static <T> void addJEISlot(IRecipeLayoutBuilder builder, EntryList<T> entries, JeiRecipeViewerSlot widget,
RecipeIngredientRole role, int index) {
var type = ModularUIJeiPlugin.getRuntime().getIngredientManager()
.getIngredientTypeChecked(widget.ingredientClass());
.getIngredientTypeChecked(entries.getType());
if (type.isEmpty()) {
return;
}

Area widgetArea = widget.getArea();
IRecipeSlotBuilder slotBuilder = builder.addSlot(role, widgetArea.x, widgetArea.y);

slotBuilder.addIngredients(type.get(), widget.getIngredients().getStacks());
slotBuilder.addIngredients(type.get(), entries.getStacks());
slotBuilder.setCustomRenderer(type.get(), new IIngredientRenderer<>() {

@Override
Expand Down Expand Up @@ -111,11 +113,11 @@ public void setRecipe(IRecipeLayoutBuilder builder, T recipe, IFocusGroup focuse

MutableInt i = new MutableInt(0);
WidgetTree.foreachChildBFS(screen.getMainPanel(), widget -> {
if (!(widget instanceof IngredientProvider<?> provider)) {
if (!(widget instanceof JeiRecipeViewerSlot provider)) {
return true;
}
RecipeIngredientRole role = mapToRole(provider.getRecipeRole());
addJEISlot(builder, provider, role, i.getAndIncrement());
RecipeIngredientRole role = mapToRole(provider.recipeSlotRole());
addJEISlot(builder, provider.getValue(), provider, role, i.getAndIncrement());
return true;
}, true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,104 @@
package brachy.modularui.integration.rei;

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.screen.viewport.ModularGuiContext;
import brachy.modularui.theme.WidgetThemeEntry;

import org.apache.commons.lang3.NotImplementedException;
import me.shedaniel.math.Point;
import me.shedaniel.rei.api.client.gui.widgets.Slot;

import me.shedaniel.rei.api.client.gui.widgets.Widgets;

import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.ApiStatus;

import java.util.function.UnaryOperator;

@ApiStatus.Experimental
public class ReiRecipeViewerSlot extends RecipeViewerSlotWidget<ReiRecipeViewerSlot> {

@ApiStatus.Internal
@Getter
private Slot slotWidget;
private int x, y;

@Accessors(fluent = true)
@Getter
private RecipeSlotRole recipeSlotRole;
@Getter
private EntryList<?> value;
@Accessors(fluent = true)
@Getter
@Setter
private float chance = 1f;

public ReiRecipeViewerSlot() {
throw new NotImplementedException();
super();
slotWidget = Widgets.createSlot(new Point()).disableBackground();
recipeSlotRole = RecipeSlotRole.RENDER_ONLY;

size(18, 18);
}

@Override
public ReiRecipeViewerSlot recipeSlotRole(RecipeSlotRole recipeSlotRole) {
this.recipeSlotRole = recipeSlotRole;
rebuildReiSlot();
return getThis();
}

@Override
public <T> ReiRecipeViewerSlot value(EntryList<T> entryList) {
this.value = entryList;
rebuildReiSlot();
if (this.value.getType() == FluidStack.class) {
background(GuiTextures.SLOT_FLUID);
} else {
background(GuiTextures.SLOT_ITEM); // TODO other types
}
return getThis();
}

@SuppressWarnings("unchecked")
private void rebuildReiSlot() {
slotWidget = Widgets.createSlot(new Point()).disableBackground();
if (this.value.getType() == ItemStack.class) {
slotWidget.entries(REIStackConverter.ITEM.convertTo((EntryList<ItemStack>) this.value, chance, UnaryOperator.identity()));
} else if (this.value.getType() == FluidStack.class) {
slotWidget.entries(REIStackConverter.FLUID.convertTo((EntryList<FluidStack>) this.value, chance, UnaryOperator.identity()));
}
Comment on lines +73 to +77

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this method to avoid unchecked generic issues (you could also put it in REIStackConverter.Converter with the unary operator as an argument):

private static <T> EntryIngredient convertToReiEntry(EntryList<T> entries, float chance) {
    return REIStackConverter.getForNullable(entries.getType()).convertTo(entries, chance, UnaryOperator.identity()));
}

And then do this:

Suggested change
if (this.value.getType() == ItemStack.class) {
slotWidget.entries(REIStackConverter.ITEM.convertTo((EntryList<ItemStack>) this.value, chance, UnaryOperator.identity()));
} else if (this.value.getType() == FluidStack.class) {
slotWidget.entries(REIStackConverter.FLUID.convertTo((EntryList<FluidStack>) this.value, chance, UnaryOperator.identity()));
}
slotWidget.entries(convertToReiEntry(this.value, this.chance));

if (recipeSlotRole == RecipeSlotRole.INPUT) {
slotWidget.markInput();
} else if (recipeSlotRole == RecipeSlotRole.OUTPUT) {
slotWidget.markOutput();
} else {
slotWidget.unmarkInputOrOutput();
Comment on lines +82 to +83

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does REI not have a way to mark an ingredient as a 'catalyst'?
If not, those should be marked as input IMO.

}
}

@Override
public ReiRecipeViewerSlot chance(float chance) {
return getThis();
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);
Comment on lines +89 to +91

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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);
context.graphicsPose().pushPose();
context.graphicsPose().translate(-this.x, -this.y, 0);
this.slotWidget.render(context.getGraphics(), context.getMouseX(), context.getMouseY(), context.getRenderPartialTicks());
context.graphicsPose().popPose();

}

@Override
public Result onMousePressed(int button) {
this.slotWidget.mouseClicked(getContext().getMouseX(), getContext().getAbsMouseY(), button);
return Result.SUCCESS;
Comment on lines +96 to +97

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.slotWidget.mouseClicked(getContext().getMouseX(), getContext().getAbsMouseY(), button);
return Result.SUCCESS;
return this.slotWidget.mouseClicked(getContext().getMouseX(), getContext().getMouseY(), button) ? Result.SUCCESS : Result.ACCEPT;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, did you intend to use window-relative or absolute cursor positioning here? rn you have a mix of both in this method.

@Trinsdar Trinsdar Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly just copied the emi slot for rei and just modified what was nessecary. I would guess the same issue exists in the emi slot

}

@Override
public Result onKeyPressed(int keyCode, int scanCode, int modifiers) {
return this.slotWidget.keyPressed(keyCode, scanCode, modifiers) ? Result.SUCCESS : Result.ACCEPT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import brachy.modularui.integration.recipeviewer.handlers.fluid.EmptyFluidTank;
import brachy.modularui.integration.recipeviewer.util.RecipeScreenRenderingUtil;
import brachy.modularui.integration.rei.REIStackConverter;
import brachy.modularui.integration.rei.ReiRecipeViewerSlot;
import brachy.modularui.screen.ModularPanel;
import brachy.modularui.screen.ModularScreen;
import brachy.modularui.utils.memoization.MemoizedSupplier;
Expand All @@ -18,6 +19,8 @@
import brachy.modularui.widgets.slot.FluidSlot;
import brachy.modularui.widgets.slot.ItemSlot;

import me.shedaniel.rei.api.client.gui.widgets.Slot;

import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.network.chat.Component;
Expand Down Expand Up @@ -72,16 +75,16 @@ public ModularUIREIDisplay(ResourceLocation recipeId, Supplier<IWidget> widgetSu
}, Duration.ofSeconds(10));

WidgetTree.foreachChildBFS(widgetSupplier.get(), widget -> {
if (!(widget instanceof IngredientProvider<?> provider)) return true;
if (!(widget instanceof ReiRecipeViewerSlot provider)) return true;

RecipeSlotRole role = provider.getRecipeRole();
RecipeSlotRole role = provider.recipeSlotRole();
if (role == RecipeSlotRole.RENDER_ONLY) return true;

REIStackConverter.Converter<?> converter = REIStackConverter.getForNullable(provider.ingredientClass());
REIStackConverter.Converter<?> converter = REIStackConverter.getForNullable(provider.getValue().getType());
if (converter == null) return true;

@SuppressWarnings({"rawtypes", "unchecked"})
EntryIngredient ingredient = ((REIStackConverter.Converter) converter).convertTo(provider);
EntryIngredient ingredient = EntryIngredient.of(provider.getSlotWidget().getEntries());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does REI's Slot.getEntries() support tag ingredients? if it doesn't, you should figure out something for that so that information isn't lost.


switch (role) {
case INPUT -> inputEntries.add(ingredient);
Expand All @@ -102,44 +105,30 @@ public List<Widget> createWidgets(Rectangle bounds) {
widgets.add(new UIWrapperWidget());

WidgetTree.foreachChildBFS(this.screen.get().getMainPanel(), widget -> {
if (!(widget instanceof IngredientProvider<?> provider)) return true;
if (!(widget instanceof ReiRecipeViewerSlot provider)) return true;

RecipeSlotRole role = provider.getRecipeRole();
RecipeSlotRole role = provider.recipeSlotRole();
if (role == RecipeSlotRole.RENDER_ONLY) return true;

REIStackConverter.Converter<?> converter = REIStackConverter.getForNullable(provider.ingredientClass());
REIStackConverter.Converter<?> converter = REIStackConverter.getForNullable(provider.getValue().getType());
if (converter == null) return true;

@SuppressWarnings({"rawtypes", "unchecked"})
EntryIngredient ingredient = ((REIStackConverter.Converter) converter).convertTo(provider);
EntryIngredient ingredient = EntryIngredient.of(provider.getSlotWidget().getEntries());
Area area = widget.getArea();

EntryWidget entryWidget = new EntryWidget(new Rectangle(area.x(), area.y(), area.w(), area.h()));
// Clear the MUI slots and add EMI slots based on them.
if (provider instanceof ItemSlot itemSlot) {
itemSlot.slot(RecipeScreenRenderingUtil.EMPTY_ITEM_HANDLER, 0).invisible();
} else if (provider instanceof FluidSlot fluidSlot) {
fluidSlot.syncHandler(EmptyFluidTank.INSTANCE).invisible();
}

entryWidget.background(false)
.entries(ingredient.castAsList());
if (role == RecipeSlotRole.INPUT) {
entryWidget.markIsInput();
} else if (role == RecipeSlotRole.OUTPUT) {
entryWidget.markIsOutput();
} else {
entryWidget.unmarkInputOrOutput();
}
if (widget instanceof ITooltip<?> tooltip && tooltip.hasTooltip()) {
if (tooltip.tooltip().getRichText() instanceof RichText richText) {
Slot entryWidget = provider.getSlotWidget();
/*
if (provider.hasTooltip()) {
if (provider.tooltip().getRichText() instanceof RichText richText) {
var textList = richText.getAsText();
entryWidget.tooltipProcessor(text -> {
textList.forEach(line -> line.map(t -> text.add((Component) t), text::add));
return text;
});
}
}
*/
widgets.add(entryWidget);
return true;
}, true);
Expand Down