Skip to content

Commit

Permalink
A bunch of modular GUI fixes / improvements.
Browse files Browse the repository at this point in the history
Fixed some issues with GuiContextMenu
Fixed GuiText depth not accounting for underline's additional z offset.
Fixed slot overlay rendering.  
Fixed GuiEntityRenderer.
Added filtering to GuiList.
Support line breaks in tooltip translations.
Made some improvements to text field suggestions.
  • Loading branch information
brandon3055 committed Feb 19, 2024
1 parent c59e0a7 commit 0834135
Show file tree
Hide file tree
Showing 14 changed files with 253 additions and 65 deletions.
54 changes: 52 additions & 2 deletions src/main/java/codechicken/lib/compat/JEIPlugin.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package codechicken.lib.compat;

import codechicken.lib.CodeChickenLib;
import codechicken.lib.gui.modular.ModularGui;
import codechicken.lib.gui.modular.ModularGuiContainer;
import codechicken.lib.gui.modular.elements.GuiElement;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.handlers.IGhostIngredientHandler;
import mezz.jei.api.gui.handlers.IGuiContainerHandler;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.registration.IGuiHandlerRegistration;
import net.covers1624.quack.collection.FastStream;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
Expand All @@ -18,7 +27,8 @@
public class JEIPlugin implements IModPlugin {
private static final ResourceLocation ID = new ResourceLocation(CodeChickenLib.MOD_ID, "jei_plugin");

public JEIPlugin() {}
public JEIPlugin() {
}

@Override
public ResourceLocation getPluginUid() {
Expand All @@ -30,8 +40,48 @@ public void registerGuiHandlers(IGuiHandlerRegistration registration) {
registration.addGuiContainerHandler(ModularGuiContainer.class, new IGuiContainerHandler<>() {
@Override
public List<Rect2i> getGuiExtraAreas(ModularGuiContainer screen) {
return screen.getModularGui().getJeiExclusions().map(e -> e.getRectangle().toRect2i()).toList();
return FastStream.of(screen.getModularGui().getJeiExclusions()).map(e -> e.getRectangle().toRect2i()).toList();
}
});
registration.addGhostIngredientHandler(ModularGuiContainer.class, new IngredientDropHandler());
}

private static class IngredientDropHandler implements IGhostIngredientHandler<ModularGuiContainer> {
private ModularGui gui;
private boolean highlight = true;

@Override
public <I> List<Target<I>> getTargetsTyped(ModularGuiContainer screen, ITypedIngredient<I> ingredient, boolean doStart) {
gui = screen.getModularGui();
gui.setJeiHighlightTime(doStart ? 60 * 20 : 3 * 20);
highlight = !doStart;
if (!doStart) return Collections.emptyList();
List<Target<I>> targets = new ArrayList<>();
ingredient.getIngredient(VanillaTypes.ITEM_STACK).ifPresent(stack -> gui.getJeiDropTargets().forEach(e -> targets.add(new DropTarget<>(e))));
return targets;
}

@Override
public void onComplete() {
highlight = true;
gui.setJeiHighlightTime(0);
}

@Override
public boolean shouldHighlightTargets() {
return highlight;
}
}

private record DropTarget<I>(GuiElement<?> element) implements IGhostIngredientHandler.Target<I> {
@Override
public Rect2i getArea() {
return element.getRectangle().toRect2i();
}

@Override
public void accept(I ingredient) {
element.getJeiDropConsumer().accept((ItemStack) ingredient);
}
}
}
39 changes: 16 additions & 23 deletions src/main/java/codechicken/lib/gui/modular/ModularGui.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,15 @@
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.TriConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
* The modular gui system is built around "Gui Elements" but those elements need to be rendered by a base parent element. That's what this class is.
Expand Down Expand Up @@ -68,7 +65,7 @@ public class ModularGui implements GuiParent<ModularGui> {
private final List<TriConsumer<Integer, Integer, Integer>> preKeyPressListeners = new ArrayList<>();
private final List<TriConsumer<Integer, Integer, Integer>> postKeyPressListeners = new ArrayList<>();

private final List<GuiElement<?>> jeiExclusions = new ArrayList<>();
private int jeiHighlightTime = 0;

/**
* @param provider The gui builder that will be used to construct this modular gui when the screen is initialized.
Expand Down Expand Up @@ -208,6 +205,7 @@ public boolean vanillaSlotRendering() {
* @param buffers BufferSource can be retried from {@link GuiGraphics}
* @return A new {@link GuiRender} for the current render call.
*/
@Deprecated //If you have the GuiGraphics, use GuiRender#convert to ensure the underlying PoseStack is carried over. That will ensure things like the JEI overlay will be rendered at a
public GuiRender createRender(MultiBufferSource.BufferSource buffers) {
return new GuiRender(mc, buffers);
}
Expand All @@ -216,7 +214,7 @@ public GuiRender createRender(MultiBufferSource.BufferSource buffers) {
* Primary render method for ModularGui. The screen implementing ModularGui must call this in its render method.
* Followed by the {@link #renderOverlay(GuiRender, float)} method to handle overlay rendering.
*
* @param render A new gui render call should be constructed for each frame via {@link #createRender(MultiBufferSource.BufferSource)}
* @param render GuiRender instance converted from Minecraft's {@link GuiGraphics} via {@link GuiRender#convert(GuiGraphics)}
*/
public void render(GuiRender render, float partialTicks) {
root.clearGeometryCache();
Expand Down Expand Up @@ -260,6 +258,7 @@ public void tick() {
tickListeners.forEach(Runnable::run);
root.tick(mouseX, mouseY);
CursorHelper.setCursor(newCursor);
if (jeiHighlightTime > 0) jeiHighlightTime--;
}

/**
Expand Down Expand Up @@ -524,26 +523,20 @@ public void setCursor(ResourceLocation cursor) {
this.newCursor = cursor;
}

/**
* Add an element to the list of jei exclusions.
* Use this for any elements that render outside the normal gui bounds.
* This will ensure JEI does not try to render on top of these elements.
*/
public void jeiExclude(GuiElement<?> element) {
if (!jeiExclusions.contains(element)) {
jeiExclusions.add(element);
}
public List<GuiElement<?>> getJeiExclusions() {
return root.addJeiExclusions(new ArrayList<>());
}

/**
* Remove an element from the list of jei exclusions.
*/
public void removeJEIExclude(GuiElement<?> element) {
jeiExclusions.remove(element);
public List<GuiElement<?>> getJeiDropTargets() {
return root.addJeiDropTargets(new ArrayList<>());
}

public void setJeiHighlightTime(int jeiHighlightTime) {
this.jeiHighlightTime = jeiHighlightTime;
}

public FastStream<GuiElement<?>> getJeiExclusions() {
return FastStream.of(jeiExclusions).filter(GuiElement::isEnabled);
public int getJeiHighlightTime() {
return jeiHighlightTime;
}

/**
Expand Down
27 changes: 24 additions & 3 deletions src/main/java/codechicken/lib/gui/modular/ModularGuiContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
public class ModularGuiContainer<T extends AbstractContainerMenu> extends AbstractContainerScreen<T> implements ContainerScreenAccess<T> {

public final ModularGui modularGui;
/**
* Flag used to disable vanilla slot highlight rendering.
* */
private boolean renderingSlots = false;

public ModularGuiContainer(T containerMenu, Inventory inventory, ContainerGuiProvider<T> provider) {
super(containerMenu, inventory, Component.empty());
Expand Down Expand Up @@ -74,7 +78,7 @@ public void render(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float
if (modularGui.renderBackground()) {
renderBackground(graphics);
}
GuiRender render = modularGui.createRender(graphics.bufferSource());
GuiRender render = GuiRender.convert(graphics);//modularGui.createRender(graphics.bufferSource());
modularGui.render(render, partialTicks);

super.render(graphics, mouseX, mouseY, partialTicks);
Expand Down Expand Up @@ -125,7 +129,7 @@ protected boolean handleFloatingItemRender(GuiRender render, int mouseX, int mou
protected boolean renderHoveredStackToolTip(GuiRender guiGraphics, int mouseX, int mouseY) {
if (this.menu.getCarried().isEmpty() && this.hoveredSlot != null && this.hoveredSlot.hasItem()) {
GuiElement<?> handler = modularGui.getSlotHandler(hoveredSlot);
if (handler != null && handler.blockMouseOver(handler, mouseX, mouseY)) {
if (handler != null && (handler.blockMouseOver(handler, mouseX, mouseY) || !handler.isMouseOver())) {
return false;
}
ItemStack itemStack = this.hoveredSlot.getItem();
Expand Down Expand Up @@ -191,7 +195,11 @@ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {

@Override
public void renderSlot(GuiGraphics guiGraphics, Slot slot) {
if (modularGui.vanillaSlotRendering()) super.renderSlot(guiGraphics, slot);
if (modularGui.vanillaSlotRendering()) {
super.renderSlot(guiGraphics, slot);
} else {
renderingSlots = true;
}
}

//Modular gui friendly version of the slot render
Expand Down Expand Up @@ -240,8 +248,21 @@ public void renderSlot(GuiRender render, Slot slot) {
}
}

@Override
public boolean isHovering(Slot pSlot, double pMouseX, double pMouseY) {
boolean ret = super.isHovering(pSlot, pMouseX, pMouseY);
//Override the isHovering check before renderSlotHighlight is called.
if (ret && renderingSlots && pSlot.isActive()) {
//This breaks the default hoveredSlot assignment, so we need to handle that here.
hoveredSlot = pSlot;
return false;
}
return ret;
}

@Override //Disable vanilla title and inventory name rendering
protected void renderLabels(GuiGraphics guiGraphics, int i, int j) {
renderingSlots = false;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void render(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float
if (modularGui.renderBackground()) {
renderBackground(graphics);
}
GuiRender render = modularGui.createRender(graphics.bufferSource());
GuiRender render = GuiRender.convert(graphics);
modularGui.render(render, partialTicks);
modularGui.renderOverlay(render, partialTicks);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
import codechicken.lib.gui.modular.ModularGui;
import codechicken.lib.gui.modular.lib.Constraints;
import codechicken.lib.gui.modular.lib.geometry.GuiParent;
import net.covers1624.quack.collection.FastStream;
import net.minecraft.network.chat.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Supplier;

Expand All @@ -26,14 +22,17 @@
public class GuiContextMenu extends GuiElement<GuiContextMenu> {

private BiFunction<GuiContextMenu, Supplier<Component>, GuiButton> buttonBuilder = (menu, label) -> GuiButton.flatColourButton(menu, label, hover -> hover ? 0xFF475b6a : 0xFF151515).constrain(HEIGHT, literal(12));
private final Map<Supplier<Component>, Runnable> options = new HashMap<>();
private final Map<Supplier<Component>, Runnable> options = new LinkedHashMap<>();
private final Map<Supplier<Component>, Supplier<List<Component>>> tooltips = new HashMap<>();
private final List<GuiButton> buttons = new ArrayList<>();
private boolean closeOnItemClicked = true;
private boolean closeOnOutsideClick = true;
private boolean actionOnClick = false;

public GuiContextMenu(ModularGui gui) {
super(gui.getRoot());
jeiExclude();
setOpaque(true);
}

public static GuiContextMenu tooltipStyleMenu(GuiParent<?> parent) {
Expand All @@ -52,6 +51,16 @@ public GuiContextMenu setCloseOnOutsideClick(boolean closeOnOutsideClick) {
return this;
}

/**
* By default, the option action fires on mouse button release (like pretty much any UI button in existence)
* Calling this will change that to action on mouse button press, (What minecraft uses for its buttons)
*/
public GuiContextMenu actionOnClick() {
this.actionOnClick = true;
rebuildButtons();
return this;
}

/**
* Only height should be constrained, with will be set automatically to accommodate the provided label.
*/
Expand Down Expand Up @@ -87,16 +96,20 @@ private void rebuildButtons() {

//Menu options can be dynamic so the width constraint needs to be dynamic.
//This is probably a little expensive, but its only while a context menu is open.
constrain(WIDTH, dynamic(() -> FastStream.of(options.keySet()).map(Supplier::get).intSum(font()::width) + 6D + 4D));
constrain(WIDTH, dynamic(() -> options.keySet().stream().mapToInt(e -> font().width(e.get())).max().orElse(0) + 6D + 4D));

double height = 3;
for (Supplier<Component> label : options.keySet()) {
Runnable action = options.get(label);
GuiButton button = buttonBuilder.apply(this, label)
.onPress(action)
.constrain(TOP, relative(get(TOP), height))
.constrain(LEFT, relative(get(LEFT), 3))
.constrain(RIGHT, relative(get(RIGHT), -3));
if (actionOnClick) {
button.onClick(action);
} else {
button.onPress(action);
}
if (tooltips.containsKey(label)) {
button.setTooltip(tooltips.get(label));
}
Expand All @@ -111,10 +124,12 @@ private void rebuildButtons() {
public boolean mouseClicked(double mouseX, double mouseY, int button, boolean consumed) {
consumed = super.mouseClicked(mouseX, mouseY, button, consumed);
if (isMouseOver() || consumed) {
if (consumed && closeOnItemClicked) {
close();
if (actionOnClick) {
if (consumed && closeOnItemClicked) {
close();
}
return true;
}
return true;
} else if (closeOnOutsideClick) {
close();
return true;
Expand All @@ -123,6 +138,20 @@ public boolean mouseClicked(double mouseX, double mouseY, int button, boolean co
return consumed;
}

@Override
public boolean mouseReleased(double mouseX, double mouseY, int button, boolean consumed) {
consumed = super.mouseReleased(mouseX, mouseY, button, consumed);
if (isMouseOver() || consumed) {
if (!actionOnClick) {
if (consumed && closeOnItemClicked) {
close();
}
return true;
}
}
return consumed;
}

public void close() {
getParent().removeChild(this);
}
Expand Down
Loading

0 comments on commit 0834135

Please sign in to comment.