Skip to content

Commit

Permalink
Add API to allow utility mods to set areas where JEI should not draw
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Dec 3, 2018
1 parent b517f23 commit 2d10205
Show file tree
Hide file tree
Showing 22 changed files with 464 additions and 330 deletions.
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ mcp_mappings=stable_nodoc_39
curse_project_id=238222

version_major=4
version_minor=13
version_patch=1
version_minor=14
version_patch=0
8 changes: 8 additions & 0 deletions src/api/java/mezz/jei/api/IModRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import mezz.jei.api.gui.IAdvancedGuiHandler;
import mezz.jei.api.gui.IGhostIngredientHandler;
import mezz.jei.api.gui.IGlobalGuiHandler;
import mezz.jei.api.gui.IGuiScreenHandler;
import mezz.jei.api.ingredients.IIngredientRegistry;
import mezz.jei.api.recipe.IIngredientType;
Expand Down Expand Up @@ -89,6 +90,13 @@ public interface IModRegistry {
*/
void addAdvancedGuiHandlers(IAdvancedGuiHandler<?>... advancedGuiHandlers);

/**
* Add a handler to give JEI extra information about how to layout the item list.
* Used for guis that display next to GUIs and would normally intersect with JEI.
* @since JEI 4.14.0
*/
void addGlobalGuiHandlers(IGlobalGuiHandler... globalGuiHandlers);

/**
* Add a handler to let JEI draw next to a specific class (or subclass) of {@link GuiScreen}.
* By default, JEI can only draw next to {@link GuiContainer}.
Expand Down
47 changes: 47 additions & 0 deletions src/api/java/mezz/jei/api/gui/IGlobalGuiHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package mezz.jei.api.gui;

import mezz.jei.api.IModRegistry;
import mezz.jei.api.ingredients.IModIngredientRegistration;

import javax.annotation.Nullable;
import java.awt.Rectangle;
import java.util.Collection;
import java.util.Collections;

/**
* Allows plugins to change how JEI is displayed next to guis.
* This is for mods that display next to all GUIs, like JEI does, so they can draw together correctly.
* For handling modded GUIs, you should use {@link IAdvancedGuiHandler} instead.
*
* Register your implementation with {@link IModRegistry#addGlobalGuiHandlers(IGlobalGuiHandler...)}.
*
* @see IAdvancedGuiHandler
* @since JEI 4.14.0
*/
public interface IGlobalGuiHandler {
/**
* Give JEI information about extra space that your mod takes up.
* Used for moving JEI out of the way of extra things like gui buttons.
*
* @return the space that the gui takes up besides the normal rectangle defined by GuiContainer.
*/
default Collection<Rectangle> getGuiExtraAreas() {
return Collections.emptyList();
}

/**
* Return anything under the mouse that JEI could not normally detect, used for JEI recipe lookups.
* <p>
* This is useful for guis that don't have normal slots (which is how JEI normally detects items under the mouse).
* <p>
* This can also be used to let JEI look up liquids in tanks directly, by returning a FluidStack.
* Works with any ingredient type that has been registered with {@link IModIngredientRegistration}.
*
* @param mouseX the current X position of the mouse in screen coordinates.
* @param mouseY the current Y position of the mouse in screen coordinates.
*/
@Nullable
default Object getIngredientUnderMouse(int mouseX, int mouseY) {
return null;
}
}
178 changes: 178 additions & 0 deletions src/main/java/mezz/jei/gui/GuiScreenHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package mezz.jei.gui;

import mezz.jei.api.gui.IAdvancedGuiHandler;
import mezz.jei.api.gui.IGhostIngredientHandler;
import mezz.jei.api.gui.IGlobalGuiHandler;
import mezz.jei.api.gui.IGuiProperties;
import mezz.jei.api.gui.IGuiScreenHandler;
import mezz.jei.ingredients.IngredientRegistry;
import mezz.jei.input.ClickedIngredient;
import mezz.jei.input.IClickedIngredient;
import mezz.jei.util.MathUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;

import javax.annotation.Nullable;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class GuiScreenHelper {
private final IngredientRegistry ingredientRegistry;
private final List<IGlobalGuiHandler> globalGuiHandlers;
private final List<IAdvancedGuiHandler<?>> advancedGuiHandlers;
private final Map<Class, IGhostIngredientHandler> ghostIngredientHandlers;
private final Map<Class, IGuiScreenHandler> guiScreenHandlers;
private Set<Rectangle> guiExclusionAreas = Collections.emptySet();

public GuiScreenHelper(IngredientRegistry ingredientRegistry, List<IGlobalGuiHandler> globalGuiHandlers, List<IAdvancedGuiHandler<?>> advancedGuiHandlers, Map<Class, IGhostIngredientHandler> ghostIngredientHandlers, Map<Class, IGuiScreenHandler> guiScreenHandlers) {
this.ingredientRegistry = ingredientRegistry;
this.globalGuiHandlers = globalGuiHandlers;
this.advancedGuiHandlers = advancedGuiHandlers;
this.ghostIngredientHandlers = ghostIngredientHandlers;
this.guiScreenHandlers = guiScreenHandlers;
}

@Nullable
public <T extends GuiScreen> IGuiProperties getGuiProperties(@Nullable T guiScreen) {
if (guiScreen == null) {
return null;
}
{
@SuppressWarnings("unchecked")
IGuiScreenHandler<T> handler = (IGuiScreenHandler<T>) guiScreenHandlers.get(guiScreen.getClass());
if (handler != null) {
return handler.apply(guiScreen);
}
}
for (Map.Entry<Class, IGuiScreenHandler> entry : guiScreenHandlers.entrySet()) {
Class guiScreenClass = entry.getKey();
if (guiScreenClass.isInstance(guiScreen)) {
@SuppressWarnings("unchecked")
IGuiScreenHandler<T> handler = entry.getValue();
if (handler != null) {
return handler.apply(guiScreen);
}
}
}
return null;
}

public boolean updateGuiExclusionAreas() {
Set<Rectangle> guiAreas = getPluginsExclusionAreas();
if (!guiAreas.equals(this.guiExclusionAreas)) {
this.guiExclusionAreas = guiAreas;
return true;
}
return false;
}

public Set<Rectangle> getGuiExclusionAreas() {
return guiExclusionAreas;
}

public boolean isInGuiExclusionArea(int mouseX, int mouseY) {
return MathUtil.contains(guiExclusionAreas, mouseX, mouseY);
}

private Set<Rectangle> getPluginsExclusionAreas() {
GuiScreen guiScreen = Minecraft.getMinecraft().currentScreen;
if (guiScreen == null) {
return Collections.emptySet();
}
Set<Rectangle> allGuiExtraAreas = new HashSet<>();
if (guiScreen instanceof GuiContainer) {
GuiContainer guiContainer = (GuiContainer) guiScreen;
List<IAdvancedGuiHandler<GuiContainer>> activeAdvancedGuiHandlers = getActiveAdvancedGuiHandlers(guiContainer);
for (IAdvancedGuiHandler<GuiContainer> advancedGuiHandler : activeAdvancedGuiHandlers) {
List<Rectangle> guiExtraAreas = advancedGuiHandler.getGuiExtraAreas(guiContainer);
if (guiExtraAreas != null) {
allGuiExtraAreas.addAll(guiExtraAreas);
}
}
}
for (IGlobalGuiHandler globalGuiHandler : globalGuiHandlers) {
Collection<Rectangle> guiExtraAreas = globalGuiHandler.getGuiExtraAreas();
allGuiExtraAreas.addAll(guiExtraAreas);
}
return allGuiExtraAreas;
}


@Nullable
public <T extends GuiContainer> IClickedIngredient<?> getPluginsIngredientUnderMouse(T guiContainer, int mouseX, int mouseY) {
List<IAdvancedGuiHandler<T>> activeAdvancedGuiHandlers = getActiveAdvancedGuiHandlers(guiContainer);
for (IAdvancedGuiHandler<T> advancedGuiHandler : activeAdvancedGuiHandlers) {
Object clicked = advancedGuiHandler.getIngredientUnderMouse(guiContainer, mouseX, mouseY);
IClickedIngredient<?> clickedIngredient = createClickedIngredient(clicked, guiContainer);
if (clickedIngredient != null) {
return clickedIngredient;
}
}
for (IGlobalGuiHandler globalGuiHandler : globalGuiHandlers) {
Object clicked = globalGuiHandler.getIngredientUnderMouse(mouseX, mouseY);
IClickedIngredient<?> clickedIngredient = createClickedIngredient(clicked, guiContainer);
if (clickedIngredient != null) {
return clickedIngredient;
}
}
return null;
}

@Nullable
public <T extends GuiScreen> IGhostIngredientHandler<T> getGhostIngredientHandler(T guiScreen) {
{
@SuppressWarnings("unchecked")
IGhostIngredientHandler<T> handler = (IGhostIngredientHandler<T>) ghostIngredientHandlers.get(guiScreen.getClass());
if (handler != null) {
return handler;
}
}
for (Map.Entry<Class, IGhostIngredientHandler> entry : ghostIngredientHandlers.entrySet()) {
Class guiScreenClass = entry.getKey();
if (guiScreenClass.isInstance(guiScreen)) {
@SuppressWarnings("unchecked")
IGhostIngredientHandler<T> handler = entry.getValue();
if (handler != null) {
return handler;
}
}
}
return null;
}

@Nullable
private <T> IClickedIngredient<T> createClickedIngredient(@Nullable T ingredient, GuiContainer guiContainer) {
if (ingredient != null && ingredientRegistry.isValidIngredient(ingredient)) {
Rectangle area = null;
Slot slotUnderMouse = guiContainer.getSlotUnderMouse();
if (ingredient instanceof ItemStack && slotUnderMouse != null && ItemStack.areItemStacksEqual(slotUnderMouse.getStack(), (ItemStack) ingredient)) {
area = new Rectangle(slotUnderMouse.xPos, slotUnderMouse.yPos, 16, 16);
}
return ClickedIngredient.create(ingredient, area);
}
return null;
}


private <T extends GuiContainer> List<IAdvancedGuiHandler<T>> getActiveAdvancedGuiHandlers(T guiContainer) {
List<IAdvancedGuiHandler<T>> activeAdvancedGuiHandler = new ArrayList<>();
for (IAdvancedGuiHandler<?> advancedGuiHandler : advancedGuiHandlers) {
Class<?> guiContainerClass = advancedGuiHandler.getGuiContainerClass();
if (guiContainerClass.isInstance(guiContainer)) {
@SuppressWarnings("unchecked")
IAdvancedGuiHandler<T> advancedGuiHandlerCast = (IAdvancedGuiHandler<T>) advancedGuiHandler;
activeAdvancedGuiHandler.add(advancedGuiHandlerCast);
}
}
return activeAdvancedGuiHandler;
}
}
38 changes: 18 additions & 20 deletions src/main/java/mezz/jei/gui/ghost/GhostIngredientDragManager.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package mezz.jei.gui.ghost;

import mezz.jei.Internal;
import mezz.jei.api.gui.IGhostIngredientHandler;
import mezz.jei.api.ingredients.IIngredientRenderer;
import mezz.jei.config.Config;
import mezz.jei.gui.GuiScreenHelper;
import mezz.jei.gui.ingredients.IIngredientListElement;
import mezz.jei.ingredients.IngredientRegistry;
import mezz.jei.input.IClickedIngredient;
import mezz.jei.runtime.JeiRuntime;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.inventory.GuiContainer;
Expand All @@ -20,6 +19,8 @@

public class GhostIngredientDragManager {
private final IGhostIngredientDragSource source;
private final GuiScreenHelper guiScreenHelper;
private final IngredientRegistry ingredientRegistry;
private final List<GhostIngredientReturning> ghostIngredientsReturning = new ArrayList<>();
@Nullable
private GhostIngredientDrag<?> ghostIngredientDrag;
Expand All @@ -28,8 +29,10 @@ public class GhostIngredientDragManager {
@Nullable
private List<IGhostIngredientHandler.Target<Object>> hoveredIngredientTargets;

public GhostIngredientDragManager(IGhostIngredientDragSource source) {
public GhostIngredientDragManager(IGhostIngredientDragSource source, GuiScreenHelper guiScreenHelper, IngredientRegistry ingredientRegistry) {
this.source = source;
this.guiScreenHelper = guiScreenHelper;
this.ingredientRegistry = ingredientRegistry;
}

public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) {
Expand All @@ -56,10 +59,9 @@ private void drawGhostIngredientHighlights(Minecraft minecraft, int mouseX, int
if (!Objects.equals(hovered, this.hoveredIngredient)) {
this.hoveredIngredient = hovered;
this.hoveredIngredientTargets = null;
JeiRuntime runtime = Internal.getRuntime();
GuiScreen currentScreen = minecraft.currentScreen;
if (runtime != null && currentScreen != null && hovered != null) {
IGhostIngredientHandler<GuiScreen> handler = runtime.getGhostIngredientHandler(currentScreen);
if (currentScreen != null && hovered != null) {
IGhostIngredientHandler<GuiScreen> handler = guiScreenHelper.getGhostIngredientHandler(currentScreen);
if (handler != null && handler.shouldHighlightTargets()) {
this.hoveredIngredientTargets = handler.getTargets(currentScreen, hovered, false);
}
Expand Down Expand Up @@ -92,20 +94,16 @@ public void stopDrag() {
}

public <T extends GuiScreen, V> boolean handleClickGhostIngredient(T currentScreen, IClickedIngredient<V> clicked) {
JeiRuntime runtime = Internal.getRuntime();
if (runtime != null) {
IGhostIngredientHandler<T> handler = runtime.getGhostIngredientHandler(currentScreen);
if (handler != null) {
V ingredient = clicked.getValue();
List<IGhostIngredientHandler.Target<V>> targets = handler.getTargets(currentScreen, ingredient, true);
if (!targets.isEmpty()) {
IngredientRegistry ingredientRegistry = Internal.getIngredientRegistry();
IIngredientRenderer<V> ingredientRenderer = ingredientRegistry.getIngredientRenderer(ingredient);
Rectangle clickedArea = clicked.getArea();
this.ghostIngredientDrag = new GhostIngredientDrag<>(handler, targets, ingredientRenderer, ingredient, clickedArea);
clicked.onClickHandled();
return true;
}
IGhostIngredientHandler<T> handler = guiScreenHelper.getGhostIngredientHandler(currentScreen);
if (handler != null) {
V ingredient = clicked.getValue();
List<IGhostIngredientHandler.Target<V>> targets = handler.getTargets(currentScreen, ingredient, true);
if (!targets.isEmpty()) {
IIngredientRenderer<V> ingredientRenderer = ingredientRegistry.getIngredientRenderer(ingredient);
Rectangle clickedArea = clicked.getArea();
this.ghostIngredientDrag = new GhostIngredientDrag<>(handler, targets, ingredientRenderer, ingredient, clickedArea);
clicked.onClickHandled();
return true;
}
}
return false;
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/mezz/jei/gui/overlay/GuiProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,30 @@ public static GuiProperties create(GuiContainer guiContainer) {
}

public static GuiProperties create(RecipesGui recipesGui) {
int extraWidth = recipesGui.getRecipeCatalystExtraWidth();
return new GuiProperties(
recipesGui.getClass(),
recipesGui.getGuiLeft(),
recipesGui.getGuiLeft() - extraWidth,
recipesGui.getGuiTop(),
recipesGui.getXSize(),
recipesGui.getXSize() + extraWidth,
recipesGui.getYSize(),
recipesGui.width,
recipesGui.height
);
}

public static boolean areEqual(@Nullable IGuiProperties a, @Nullable IGuiProperties b) {
if (a == b) {
return true;
}
return a != null && b != null &&
a.getGuiClass().equals(b.getGuiClass()) &&
a.getGuiLeft() == b.getGuiLeft() &&
a.getGuiXSize() == b.getGuiXSize() &&
a.getScreenWidth() == b.getScreenWidth() &&
a.getScreenHeight() == b.getScreenHeight();
}

private GuiProperties(Class<? extends GuiScreen> guiClass, int guiLeft, int guiTop, int guiXSize, int guiYSize, int screenWidth, int screenHeight) {
this.guiClass = guiClass;
this.guiLeft = guiLeft;
Expand Down
Loading

0 comments on commit 2d10205

Please sign in to comment.