mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-12 16:37:45 +00:00
1011 lines
36 KiB
Java
1011 lines
36 KiB
Java
package codechicken.nei.recipe;
|
|
|
|
import java.awt.Point;
|
|
import java.awt.Rectangle;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.regex.Matcher;
|
|
|
|
import net.minecraft.client.gui.GuiButton;
|
|
import net.minecraft.client.gui.GuiScreen;
|
|
import net.minecraft.client.gui.inventory.GuiContainer;
|
|
import net.minecraft.client.renderer.OpenGlHelper;
|
|
import net.minecraft.client.renderer.RenderHelper;
|
|
import net.minecraft.inventory.Slot;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.util.EnumChatFormatting;
|
|
|
|
import org.lwjgl.input.Keyboard;
|
|
import org.lwjgl.opengl.GL11;
|
|
|
|
import codechicken.lib.gui.GuiDraw;
|
|
import codechicken.lib.vec.Rectangle4i;
|
|
import codechicken.nei.Button;
|
|
import codechicken.nei.GuiNEIButton;
|
|
import codechicken.nei.NEICPH;
|
|
import codechicken.nei.NEIClientConfig;
|
|
import codechicken.nei.NEIClientUtils;
|
|
import codechicken.nei.PositionedStack;
|
|
import codechicken.nei.RecipeSearchField;
|
|
import codechicken.nei.RestartableTask;
|
|
import codechicken.nei.SearchField;
|
|
import codechicken.nei.VisiblityData;
|
|
import codechicken.nei.Widget;
|
|
import codechicken.nei.api.IGuiContainerOverlay;
|
|
import codechicken.nei.api.INEIGuiHandler;
|
|
import codechicken.nei.api.IRecipeFilter;
|
|
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
|
import codechicken.nei.api.ItemFilter;
|
|
import codechicken.nei.api.TaggedInventoryArea;
|
|
import codechicken.nei.drawable.DrawableBuilder;
|
|
import codechicken.nei.drawable.DrawableResource;
|
|
import codechicken.nei.filter.AllMultiRecipeFilter;
|
|
import codechicken.nei.guihook.GuiContainerManager;
|
|
import codechicken.nei.guihook.IContainerTooltipHandler;
|
|
import codechicken.nei.guihook.IGuiClientSide;
|
|
import codechicken.nei.guihook.IGuiHandleMouseWheel;
|
|
import codechicken.nei.recipe.Recipe.RecipeId;
|
|
import codechicken.nei.scroll.ScrollBar;
|
|
import codechicken.nei.scroll.ScrollBar.OverflowType;
|
|
import codechicken.nei.scroll.ScrollBar.ScrollPlace;
|
|
import codechicken.nei.scroll.ScrollContainer;
|
|
import codechicken.nei.util.SlotInaccessible;
|
|
|
|
public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer implements IGuiContainerOverlay,
|
|
IGuiClientSide, IGuiHandleMouseWheel, IContainerTooltipHandler, INEIGuiHandler {
|
|
|
|
private static final int BORDER_PADDING = 5;
|
|
private static final int TRANSPARENCY_BORDER = 4;
|
|
private final DrawableResource BG_TEXTURE = new DrawableBuilder(
|
|
"nei:textures/gui/recipebg.png",
|
|
0,
|
|
0,
|
|
176 + TRANSPARENCY_BORDER * 2,
|
|
166 + TRANSPARENCY_BORDER * 2).build();
|
|
|
|
private static final int BUTTON_WIDTH = 13;
|
|
private static final int BUTTON_HEIGHT = 12;
|
|
|
|
protected static final ScrollBar VERTICAL_SCROLLBAR = new ScrollBar().setTrackWidth(14)
|
|
.setOverflowType(OverflowType.AUTO).setScrollPlace(ScrollPlace.END)
|
|
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).build(), 9, 9)
|
|
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).build(), 3, 2)
|
|
.setThumbPadding(1, 6, 0, 6);
|
|
|
|
public static final List<IRecipeFilterProvider> recipeFilterers = new LinkedList<>();
|
|
|
|
// some mods reflect this properties
|
|
public ArrayList<H> currenthandlers = new ArrayList<>();
|
|
public int page;
|
|
public int recipetype;
|
|
|
|
private RecipeId recipeId;
|
|
public GuiContainer firstGui;
|
|
public GuiScreen firstGuiGeneral;
|
|
public GuiScreen prevGui;
|
|
private GuiButton nextpage;
|
|
private GuiButton prevpage;
|
|
private GuiButton prevtype;
|
|
private GuiButton nexttype;
|
|
|
|
private int lastPage = -1;
|
|
private final GuiRecipeTabs recipeTabs;
|
|
private final GuiRecipeCatalyst recipeCatalyst;
|
|
private SearchRecipeHandler<H> handler;
|
|
private HandlerInfo handlerInfo;
|
|
private RecipePageManager handlerPages;
|
|
|
|
protected static final RestartableTask updateFilter = new RestartableTask("NEI Recipe Filtering") {
|
|
|
|
@Override
|
|
public void execute() {
|
|
final GuiScreen currentScreen = NEIClientUtils.mc().currentScreen;
|
|
|
|
if (currentScreen instanceof GuiRecipe<?>guiRecipe) {
|
|
final SearchRecipeHandler<?> searchHandler = guiRecipe.handler;
|
|
|
|
if (searchHandler != null && searchHandler.searchingAvailable()) {
|
|
|
|
if (GuiRecipe.searchField.text().isEmpty()) {
|
|
searchHandler.setSearchIndices(null);
|
|
} else {
|
|
final IRecipeFilter filter = GuiRecipe.searchField.getRecipeFilter();
|
|
final List<Integer> filtered = searchHandler.getSearchResult(filter);
|
|
|
|
if (filtered == null) {
|
|
stop();
|
|
}
|
|
|
|
if (interrupted()) return;
|
|
searchHandler.setSearchIndices(filtered);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
protected static final RecipeSearchField searchField = new RecipeSearchField("") {
|
|
|
|
@Override
|
|
protected boolean noResults() {
|
|
final GuiScreen currentScreen = NEIClientUtils.mc().currentScreen;
|
|
return !(currentScreen instanceof GuiRecipe)
|
|
|| ((GuiRecipe<?>) currentScreen).handlerPages.getNumRecipes() > 0;
|
|
}
|
|
|
|
@Override
|
|
public void onTextChange(String oldText) {
|
|
updateFilter.restart();
|
|
}
|
|
|
|
};
|
|
|
|
protected static final Button toggleSearch = new Button() {
|
|
|
|
@Override
|
|
public boolean onButtonPress(boolean rightclick) {
|
|
if (rightclick) return false;
|
|
|
|
if (GuiRecipe.searchField.isVisible()) {
|
|
GuiRecipe.searchField.setText("");
|
|
GuiRecipe.searchField.setFocus(false);
|
|
GuiRecipe.searchField.setVisible(false);
|
|
state = 0;
|
|
} else {
|
|
GuiRecipe.searchField.setVisible(true);
|
|
GuiRecipe.searchField.setFocus(true);
|
|
state = 2;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* This will only be true iff height hacking has been configured for the current recipe handler AND we are currently
|
|
* within the scope of an active {@link CompatibilityHacks} instance.
|
|
*/
|
|
private boolean isHeightHackApplied = false;
|
|
|
|
protected GuiRecipe(GuiScreen prevgui) {
|
|
super(new ContainerRecipe());
|
|
this.recipeTabs = new GuiRecipeTabs() {
|
|
|
|
@Override
|
|
protected void setRecipePage(int recipetype) {
|
|
GuiRecipe.this.setRecipePage(recipetype);
|
|
}
|
|
|
|
};
|
|
this.recipeCatalyst = new GuiRecipeCatalyst();
|
|
this.slotcontainer = (ContainerRecipe) this.inventorySlots;
|
|
|
|
this.prevGui = prevgui;
|
|
this.firstGuiGeneral = prevgui;
|
|
|
|
if (prevgui instanceof GuiContainer firstGui) {
|
|
this.firstGui = firstGui;
|
|
}
|
|
|
|
if (prevgui instanceof IGuiContainerOverlay gui) {
|
|
this.firstGui = gui.getFirstScreen();
|
|
this.firstGuiGeneral = gui.getFirstScreenGeneral();
|
|
}
|
|
|
|
this.prevtype = new GuiNEIButton(0, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "<") {
|
|
|
|
@Override
|
|
public void mouseReleased(int mouseX, int mouseY) {
|
|
prevType();
|
|
}
|
|
};
|
|
this.nexttype = new GuiNEIButton(1, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, ">") {
|
|
|
|
@Override
|
|
public void mouseReleased(int mouseX, int mouseY) {
|
|
nextType();
|
|
}
|
|
};
|
|
|
|
this.prevpage = new GuiNEIButton(2, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "<") {
|
|
|
|
@Override
|
|
public void mouseReleased(int mouseX, int mouseY) {
|
|
prevPage();
|
|
}
|
|
};
|
|
this.nextpage = new GuiNEIButton(3, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, ">") {
|
|
|
|
@Override
|
|
public void mouseReleased(int mouseX, int mouseY) {
|
|
nextPage();
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Many old mods assumed a fixed NEI window height of {@code 166} pixels. Now that this is no longer the case, their
|
|
* tooltip and click zone handling is broken. This helper class fixes these old mods by hacking the {@link #height}
|
|
* property's value so that these old mods' calculations return the correct value for the new height.
|
|
*
|
|
* <p>
|
|
* New and old mods often assume that the handler is only drawn when {@link GuiRecipe} is the currently active
|
|
* screen in {@link net.minecraft.client.Minecraft}. This is no longer true since the addition of recipe tooltips
|
|
* which need to render the recipe in other containers on mouse hover, this class temporarily switches the active
|
|
* screen to {@code this} while rendering them to avoid ClassCastExceptions and other similar crashes.
|
|
*
|
|
* <p>
|
|
* This class is an {@link AutoCloseable} so that it can be used with try-with-resources, which will ensure that
|
|
* {@link #height} and {@link net.minecraft.client.Minecraft#currentScreen} is returned to the correct value
|
|
* afterwards.
|
|
*/
|
|
private class CompatibilityHacks implements AutoCloseable {
|
|
|
|
private final int trueHeight;
|
|
private final int trueGuiTop;
|
|
|
|
private CompatibilityHacks() {
|
|
trueHeight = height;
|
|
trueGuiTop = guiTop;
|
|
|
|
isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream()
|
|
.map(pattern -> pattern.matcher(handler.original.getHandlerId())).anyMatch(Matcher::matches);
|
|
if (isHeightHackApplied) {
|
|
// guiTop is the top edge of the recipe screen on the y-axis. Old recipe
|
|
// handlers expect a single paging
|
|
// widget at the top and at the bottom of the recipe screen with a height of
|
|
// 16px each, but GTNH NEI
|
|
// moved the bottom paging widget to the top following JEI's design, increasing
|
|
// the "forbidden" zone at
|
|
// the top to 32px. To fix these old recipe handlers we move guiTop down 16px so
|
|
// that they can keep
|
|
// working with the old 16px gap.
|
|
guiTop += 16;
|
|
|
|
// The old NEI recipe screen had a fixed width and height and was always
|
|
// centered on the screen. Legacy
|
|
// recipe handlers use the calculation ((height - 166) / 2) to compute the
|
|
// y-value of the top edge of
|
|
// the NEI window. GTNH NEI changes the layout so that the recipe screen length
|
|
// now has a flexible
|
|
// length and is no longer centered vertically. In order for the top-of-screen
|
|
// calculation to return the
|
|
// correct result, we have to hack the height field with an inverse of the
|
|
// calculation using the actual
|
|
// top of the recipe screen stored in guiTop.
|
|
height = (2 * guiTop) + 166;
|
|
|
|
// For reference, in case we ever need to modify width as well:
|
|
// the legacy calculation used for width is ((width - 176) / 2), which should
|
|
// evaluate to be equal to
|
|
// guiWidth (the x-value of the left edge of the NEI recipe screen). So if we
|
|
// wanted to override width
|
|
// as well, we'd do this:
|
|
// width = (2 * guiWidth) + 176;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() {
|
|
guiTop = trueGuiTop;
|
|
height = trueHeight;
|
|
|
|
isHeightHackApplied = false;
|
|
}
|
|
}
|
|
|
|
private ScrollContainer container = new ScrollContainer() {
|
|
|
|
@Override
|
|
public Widget getWidgetUnderMouse(int mousex, int mousey) {
|
|
|
|
if (handlerInfo.getUseCustomScroll() && !this.widgets.isEmpty()) {
|
|
return this.widgets.get(0);
|
|
}
|
|
|
|
return super.getWidgetUnderMouse(mousex, mousey);
|
|
}
|
|
|
|
};
|
|
|
|
@Override
|
|
public void initGui() {
|
|
final int tabHeight = GuiRecipeTabs.getTabHeight();
|
|
this.xSize = 176;
|
|
this.ySize = Math
|
|
.min(this.height - 22 - 22 - tabHeight, NEIClientConfig.getIntSetting("inventory.guirecipe.maxHeight"));
|
|
|
|
super.initGui();
|
|
|
|
this.guiTop = Math.max(22 - 3 + tabHeight, (this.height - this.ySize) / 2);
|
|
|
|
if (this.handler == null) {
|
|
setRecipePage(this.recipetype);
|
|
}
|
|
|
|
final int rightButtonX = this.guiLeft + this.xSize - BORDER_PADDING - BUTTON_WIDTH + 1;
|
|
final int leftButtonX = this.guiLeft + BORDER_PADDING;
|
|
|
|
this.prevtype.xPosition = leftButtonX;
|
|
this.prevtype.yPosition = guiTop + 3;
|
|
this.nexttype.xPosition = rightButtonX;
|
|
this.nexttype.yPosition = guiTop + 3;
|
|
this.prevpage.xPosition = leftButtonX;
|
|
this.prevpage.yPosition = guiTop + 17;
|
|
this.nextpage.xPosition = rightButtonX;
|
|
this.nextpage.yPosition = guiTop + 17;
|
|
|
|
this.container.x = this.guiLeft + 3;
|
|
this.container.y = this.guiTop + 32;
|
|
this.container.h = this.ySize - 32 - 5;
|
|
|
|
GuiRecipe.toggleSearch.icon = new DrawableBuilder("nei:textures/nei_sprites.png", 0, 76, 10, 10).build();
|
|
GuiRecipe.toggleSearch.w = GuiRecipe.toggleSearch.h = 12;
|
|
GuiRecipe.toggleSearch.x = this.guiLeft + BORDER_PADDING + BUTTON_WIDTH;
|
|
GuiRecipe.toggleSearch.y = this.guiTop + 17;
|
|
|
|
GuiRecipe.searchField.y = this.guiTop + 16;
|
|
GuiRecipe.searchField.x = this.guiLeft + BORDER_PADDING + BUTTON_WIDTH + GuiRecipe.toggleSearch.w;
|
|
GuiRecipe.searchField.w = this.xSize - (BORDER_PADDING + BUTTON_WIDTH) * 2 + 1 - GuiRecipe.toggleSearch.w - 45;
|
|
GuiRecipe.searchField.h = 14;
|
|
|
|
this.buttonList.addAll(Arrays.asList(this.prevtype, this.nexttype, this.prevpage, this.nextpage));
|
|
|
|
if (this.currenthandlers.size() == 1) {
|
|
this.prevtype.visible = false;
|
|
this.nexttype.visible = false;
|
|
}
|
|
|
|
this.recipeTabs.update(this);
|
|
}
|
|
|
|
protected void refreshContainer() {
|
|
|
|
if (this.handlerPages.rebuildPages() || this.lastPage != this.handlerPages.getCurrentPageIndex()) {
|
|
this.lastPage = this.handlerPages.getCurrentPageIndex();
|
|
this.container.setWidgets(this.handlerPages.getCurrentPageWidgets());
|
|
|
|
if (this.handlerInfo.getUseCustomScroll()) {
|
|
this.container.setVerticalScroll(null);
|
|
} else if (this.container.w == this.xSize - 6) {
|
|
this.container.setVerticalScroll(VERTICAL_SCROLLBAR);
|
|
} else {
|
|
this.container.setVerticalScroll(
|
|
ScrollBar.defaultVerticalBar().setOverflowType(OverflowType.OVERLAY)
|
|
.setTrackPadding(-8, 0, 0, 0));
|
|
}
|
|
}
|
|
|
|
this.recipeCatalyst.setCatalysts(RecipeCatalysts.getRecipeCatalysts(this.handler.original));
|
|
this.container.update();
|
|
}
|
|
|
|
public static IRecipeFilter getRecipeListFilter() {
|
|
if (recipeFilterers.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
final AllMultiRecipeFilter recipeFilter = new AllMultiRecipeFilter();
|
|
|
|
synchronized (recipeFilterers) {
|
|
for (IRecipeFilterProvider p : recipeFilterers) {
|
|
IRecipeFilter filter = p.getRecipeFilter();
|
|
if (filter != null) {
|
|
recipeFilter.filters.add(filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
return recipeFilter.filters.size() == 1 ? recipeFilter.filters.get(0) : recipeFilter;
|
|
}
|
|
|
|
public static ItemFilter getSearchItemFilter() {
|
|
return GuiRecipe.searchField.getFilter();
|
|
}
|
|
|
|
protected void setRecipePage(int idx) {
|
|
this.recipetype = (this.currenthandlers.size() + idx) % this.currenthandlers.size();
|
|
this.handler = new SearchRecipeHandler<>(this.currenthandlers.get(this.recipetype));
|
|
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.handler.original);
|
|
this.container.w = Math.max(this.xSize - 6, this.handlerInfo.getWidth());
|
|
this.handlerPages = new RecipePageManager(this.handler, this.handlerInfo, this.container);
|
|
this.prevpage.enabled = this.nextpage.enabled = this.handlerPages.getNumPages() > 1;
|
|
this.lastPage = -1;
|
|
|
|
GuiRecipe.searchField.setText("");
|
|
GuiRecipe.searchField.setVisible(false);
|
|
GuiRecipe.toggleSearch.state = 0;
|
|
|
|
this.container.setVerticalScrollOffset(0);
|
|
this.recipeTabs.update(this);
|
|
}
|
|
|
|
public int openTargetRecipe(RecipeId recipeId) {
|
|
int refIndex = -1;
|
|
int recipetype = 0;
|
|
|
|
this.recipeId = recipeId;
|
|
|
|
if (this.recipeId != null) {
|
|
for (int j = 0; j < this.currenthandlers.size(); j++) {
|
|
final H localHandler = this.currenthandlers.get(j);
|
|
final HandlerInfo localHandlerInfo = GuiRecipeTab.getHandlerInfo(localHandler);
|
|
|
|
if (localHandlerInfo.getHandlerName().equals(this.recipeId.getHandleName())) {
|
|
recipetype = j;
|
|
|
|
if (!this.recipeId.getIngredients().isEmpty()) {
|
|
refIndex = SearchRecipeHandler.findFirst(
|
|
localHandler,
|
|
recipeIndex -> this.recipeId
|
|
.equalsIngredients(localHandler.getIngredientStacks(recipeIndex)));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
setRecipePage(recipetype);
|
|
this.handlerPages.gotoRefIndex(Math.max(0, refIndex));
|
|
|
|
return refIndex;
|
|
}
|
|
|
|
public Recipe getFocusedRecipe() {
|
|
final Point mouse = GuiDraw.getMousePosition();
|
|
final Widget activeWidget = this.container.getWidgetUnderMouse(mouse.x, mouse.y);
|
|
|
|
if (activeWidget instanceof NEIRecipeWidget recipeWidget && recipeWidget.isFocusedRecipe(mouse.x, mouse.y)) {
|
|
return recipeWidget.getRecipe();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public boolean isMouseOver(PositionedStack stack, int refIndex) {
|
|
final Point mouse = GuiDraw.getMousePosition();
|
|
final Widget activeWidget = this.container.getWidgetUnderMouse(mouse.x, mouse.y);
|
|
|
|
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
|
|
final PositionedStack hovered = recipeWidget.getPositionedStackMouseOver(mouse.x, mouse.y);
|
|
|
|
if (hovered != null) {
|
|
return stack.relx == hovered.relx && stack.rely == hovered.rely;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Slot getSlotAtPosition(int mousex, int mousey) {
|
|
final Widget activeWidget = this.container.getWidgetUnderMouse(mousex, mousey);
|
|
final ContainerRecipe slotcontainer = (ContainerRecipe) inventorySlots;
|
|
slotcontainer.setActiveStack(null);
|
|
|
|
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
|
|
final PositionedStack hovered = recipeWidget.getPositionedStackMouseOver(mousex, mousey);
|
|
|
|
if (hovered != null) {
|
|
slotcontainer.setActiveStack(hovered.item);
|
|
return new SlotInaccessible(hovered.item, hovered.relx, hovered.rely);
|
|
}
|
|
}
|
|
|
|
final PositionedStack activeStack = this.recipeCatalyst.getPositionedStackMouseOver(mousex, mousey);
|
|
|
|
if (activeStack != null) {
|
|
slotcontainer.setActiveStack(activeStack.item);
|
|
return new SlotInaccessible(activeStack.item, activeStack.relx, activeStack.rely);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public String getHandlerName() {
|
|
return this.handlerInfo.getHandlerName();
|
|
}
|
|
|
|
public H getHandler() {
|
|
return this.handler.original;
|
|
}
|
|
|
|
public List<Integer> getRecipeIndices() {
|
|
return this.handlerPages.getRecipeIndices();
|
|
}
|
|
|
|
@Override
|
|
public void keyTyped(char c, int i) {
|
|
|
|
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.focused()
|
|
&& GuiRecipe.searchField.handleKeyPress(i, c)) {
|
|
return;
|
|
}
|
|
|
|
if (i == Keyboard.KEY_ESCAPE) { // esc
|
|
this.mc.displayGuiScreen(getFirstScreenGeneral());
|
|
NEICPH.sendRequestContainer();
|
|
return;
|
|
}
|
|
|
|
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.focused()) {
|
|
GuiRecipe.searchField.lastKeyTyped(i, c);
|
|
return;
|
|
}
|
|
|
|
if (this.recipeCatalyst.isShowWidget()) {
|
|
if (this.recipeCatalyst.handleKeyPress(i, c)) {
|
|
return;
|
|
}
|
|
|
|
this.recipeCatalyst.lastKeyTyped(i, c);
|
|
}
|
|
|
|
if (this.container.handleKeyPress(i, c)) {
|
|
return;
|
|
}
|
|
|
|
this.container.lastKeyTyped(i, c);
|
|
|
|
if (GuiContainerManager.getManager(this).lastKeyTyped(i, c)) {
|
|
return;
|
|
}
|
|
|
|
if (i == mc.gameSettings.keyBindInventory.getKeyCode()) {
|
|
this.mc.displayGuiScreen(getFirstScreenGeneral());
|
|
NEICPH.sendRequestContainer();
|
|
} else if (NEIClientConfig.isKeyHashDown("gui.back")) {
|
|
this.mc.displayGuiScreen(this.prevGui);
|
|
} else if (NEIClientConfig.isKeyHashDown("gui.prev_machine")) {
|
|
prevType();
|
|
} else if (NEIClientConfig.isKeyHashDown("gui.next_machine")) {
|
|
nextType();
|
|
} else if (NEIClientConfig.isKeyHashDown("gui.prev_recipe")) {
|
|
prevPage();
|
|
} else if (NEIClientConfig.isKeyHashDown("gui.next_recipe")) {
|
|
nextPage();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void mouseClicked(int mousex, int mousey, int button) {
|
|
|
|
if (this.handler != null && this.handler.searchingAvailable()) {
|
|
if (GuiRecipe.toggleSearch.contains(mousex, mousey)) {
|
|
GuiRecipe.toggleSearch.handleClick(mousex, mousey, button);
|
|
} else if (GuiRecipe.searchField.contains(mousex, mousey)) {
|
|
GuiRecipe.searchField.handleClick(mousex, mousey, button);
|
|
} else {
|
|
GuiRecipe.searchField.onGuiClick(mousex, mousey);
|
|
}
|
|
}
|
|
|
|
if (this.recipeCatalyst.isShowWidget() && this.recipeCatalyst.handleClick(mousex, mousey, button)) {
|
|
return;
|
|
}
|
|
|
|
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
|
if (this.container.handleClick(mousex, mousey, button)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (this.recipeTabs.mouseClicked(mousex, mousey, button)) {
|
|
return;
|
|
}
|
|
|
|
if (button == 0 && isHandlerTitleHovered(mousex, mousey)) {
|
|
if (this instanceof GuiCraftingRecipe) {
|
|
GuiCraftingRecipe.openRecipeGui("all");
|
|
} else if (this instanceof GuiUsageRecipe) {
|
|
GuiUsageRecipe.openRecipeGui("all");
|
|
}
|
|
return;
|
|
}
|
|
|
|
super.mouseClicked(mousex, mousey, button);
|
|
}
|
|
|
|
@Override
|
|
protected void mouseClickMove(int mousex, int mousey, int button, long heldTime) {
|
|
if (this.recipeCatalyst.isShowWidget()) {
|
|
this.recipeCatalyst.mouseDragged(mousex, mousey, button, heldTime);
|
|
}
|
|
|
|
this.container.mouseDragged(mousex, mousey, button, heldTime);
|
|
super.mouseClickMove(mousex, mousey, button, heldTime);
|
|
}
|
|
|
|
@Override
|
|
protected void mouseMovedOrUp(int mousex, int mousey, int state) {
|
|
|
|
if (state != -1) {
|
|
|
|
if (this.recipeCatalyst.isShowWidget()) {
|
|
this.recipeCatalyst.mouseUp(mousex, mousey, state);
|
|
}
|
|
|
|
this.container.mouseUp(mousex, mousey, state);
|
|
}
|
|
|
|
super.mouseMovedOrUp(mousex, mousey, state);
|
|
}
|
|
|
|
@Override
|
|
public void mouseScrolled(int scroll) {
|
|
// Height hacking is not necessary here since mouse scrolling is a new feature,
|
|
// added in
|
|
// GTNH NEI. So no old mods will use this. Though not hacking the height here
|
|
// does mean that
|
|
// the value of the height field will be different from in other mouseover
|
|
// methods, which
|
|
// could be confusing...
|
|
|
|
// First, invoke scroll handling over recipe handler tabbar. Makes sure it is
|
|
// not overwritten by recipe
|
|
// handler-specific scroll behavior.
|
|
if (this.recipeTabs.mouseScrolled(scroll)) return;
|
|
|
|
final Point mouse = GuiDraw.getMousePosition();
|
|
|
|
if (this.recipeCatalyst.isShowWidget() && this.recipeCatalyst.onMouseWheel(scroll, mouse.x, mouse.y)) {
|
|
return;
|
|
}
|
|
|
|
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
|
if (this.container.onMouseWheel(scroll, mouse.x, mouse.y)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If shift is held, try switching to the next recipe handler. Replicates the
|
|
// GuiRecipeTabs.mouseScrolled()
|
|
// without the checking for the cursor being inside the tabbar.
|
|
if (NEIClientUtils.shiftKey()) {
|
|
if (scroll < 0) {
|
|
nextType();
|
|
} else {
|
|
prevType();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Finally, if nothing else has handled scrolling, try changing to the next
|
|
// recipe page.
|
|
if (new Rectangle(this.guiLeft, this.guiTop, this.xSize, this.ySize).contains(mouse)) {
|
|
if (scroll > 0) {
|
|
prevPage();
|
|
} else {
|
|
nextPage();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void updateScreen() {
|
|
super.updateScreen();
|
|
|
|
this.handler.original.onUpdate();
|
|
refreshContainer();
|
|
|
|
this.recipeCatalyst.setAvailableHeight(this.ySize - 5);
|
|
this.recipeCatalyst.y = this.guiTop;
|
|
this.recipeCatalyst.x = this.guiLeft - this.recipeCatalyst.w + 4;
|
|
this.recipeCatalyst.update();
|
|
}
|
|
|
|
@Override
|
|
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
|
|
|
|
if (this.recipeCatalyst.isShowWidget()) {
|
|
currenttip = this.recipeCatalyst.handleTooltip(mousex, mousey, currenttip);
|
|
}
|
|
|
|
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
|
currenttip = this.container.handleTooltip(mousex, mousey, currenttip);
|
|
}
|
|
|
|
this.recipeTabs.handleTooltip(mousex, mousey, currenttip);
|
|
|
|
if (currenttip.isEmpty() && isHandlerTitleHovered(mousex, mousey)) {
|
|
currenttip.add(NEIClientUtils.translate("recipe.tab.view_all.tooltip"));
|
|
}
|
|
|
|
if (currenttip.isEmpty() && GuiRecipe.searchField.isVisible()
|
|
&& new Rectangle(GuiRecipe.searchField.x + GuiRecipe.searchField.w, 15, 44, 16)
|
|
.contains(mousex - this.guiLeft, mousey - this.guiTop)) {
|
|
|
|
final String pageInfo = String
|
|
.format("%d/%d", this.handlerPages.getCurrentPageIndex() + 1, this.handlerPages.getNumPages());
|
|
|
|
if (this.fontRendererObj.getStringWidth(pageInfo) >= 45) {
|
|
currenttip.add(pageInfo);
|
|
}
|
|
}
|
|
|
|
return currenttip;
|
|
}
|
|
|
|
@Override
|
|
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
|
|
List<String> currenttip) {
|
|
final Widget activeWidget = this.container.getWidgetUnderMouse(mousex, mousey);
|
|
|
|
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
|
|
currenttip = recipeWidget.handleItemTooltip(itemstack, mousex, mousey, currenttip);
|
|
}
|
|
|
|
return currenttip;
|
|
}
|
|
|
|
@Override
|
|
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
|
|
|
|
if (this.recipeCatalyst.isShowWidget()) {
|
|
hotkeys = this.recipeCatalyst.handleHotkeys(mousex, mousey, hotkeys);
|
|
}
|
|
|
|
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
|
hotkeys = this.container.handleHotkeys(mousex, mousey, hotkeys);
|
|
}
|
|
|
|
return hotkeys;
|
|
}
|
|
|
|
private void nextPage() {
|
|
this.handlerPages.changePage(1);
|
|
}
|
|
|
|
private void prevPage() {
|
|
this.handlerPages.changePage(-1);
|
|
}
|
|
|
|
protected void nextType() {
|
|
setRecipePage(++recipetype);
|
|
}
|
|
|
|
protected void prevType() {
|
|
setRecipePage(--recipetype);
|
|
}
|
|
|
|
public GuiRecipeCatalyst getRecipeCatalystWidget() {
|
|
return this.recipeCatalyst;
|
|
}
|
|
|
|
protected void forceRefreshPage() {
|
|
this.currenthandlers.sort(NEIClientConfig.HANDLER_COMPARATOR);
|
|
|
|
final int currentPage = this.handlerPages.getCurrentPageIndex();
|
|
setRecipePage(this.recipetype);
|
|
this.handlerPages.changePage(currentPage);
|
|
|
|
refreshContainer();
|
|
}
|
|
|
|
@Override
|
|
public void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
|
|
GL11.glTranslatef(-guiLeft, -guiTop, 0);
|
|
this.container.draw(mouseX, mouseY);
|
|
|
|
if (NEIClientConfig.getJEIStyleRecipeCatalysts() != 0 && this.recipeCatalyst.isShowWidget()) {
|
|
this.recipeCatalyst.draw(mouseX, mouseY);
|
|
}
|
|
|
|
GL11.glTranslatef(guiLeft, guiTop, 0);
|
|
}
|
|
|
|
@Override
|
|
public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) {
|
|
BG_TEXTURE.draw(
|
|
this.guiLeft - TRANSPARENCY_BORDER,
|
|
this.guiTop - TRANSPARENCY_BORDER,
|
|
this.xSize + TRANSPARENCY_BORDER * 2,
|
|
this.ySize + TRANSPARENCY_BORDER * 2,
|
|
BORDER_PADDING + TRANSPARENCY_BORDER,
|
|
BORDER_PADDING + TRANSPARENCY_BORDER,
|
|
BORDER_PADDING + TRANSPARENCY_BORDER,
|
|
BORDER_PADDING + TRANSPARENCY_BORDER);
|
|
|
|
drawJEITabs(mouseX, mouseY);
|
|
}
|
|
|
|
private void drawJEITabs(int mouseX, int mouseY) {
|
|
final int textMiddle = (BUTTON_WIDTH - this.fontRendererObj.FONT_HEIGHT) / 2;
|
|
|
|
drawRect(
|
|
this.prevtype.xPosition + BUTTON_WIDTH - 1,
|
|
this.prevtype.yPosition,
|
|
this.nexttype.xPosition,
|
|
this.prevtype.yPosition + BUTTON_HEIGHT,
|
|
0x30000000);
|
|
drawRect(
|
|
this.prevpage.xPosition + BUTTON_WIDTH - 1,
|
|
this.prevpage.yPosition,
|
|
this.nextpage.xPosition,
|
|
this.prevpage.yPosition + BUTTON_HEIGHT,
|
|
0x30000000);
|
|
|
|
final String handlerTitle = this.handler.original.getRecipeName().trim();
|
|
final String titleColorCode = getHandlerTitleColorCode(isHandlerTitleHovered(mouseX, mouseY));
|
|
drawCenteredString(
|
|
this.fontRendererObj,
|
|
titleColorCode + handlerTitle + EnumChatFormatting.RESET,
|
|
this.guiLeft + this.xSize / 2,
|
|
this.prevtype.yPosition + textMiddle,
|
|
0xffffff);
|
|
|
|
if (this.handler.searchingAvailable()) {
|
|
GuiRecipe.toggleSearch.draw(mouseX, mouseY);
|
|
}
|
|
|
|
if (GuiRecipe.searchField.isVisible()) {
|
|
GuiRecipe.searchField.draw(mouseX, mouseY);
|
|
}
|
|
|
|
if (GuiRecipe.searchField.isVisible()) {
|
|
final String recipePage = NEIClientUtils.cropText(
|
|
this.fontRendererObj,
|
|
String.format(
|
|
"%d/%d",
|
|
this.handlerPages.getCurrentPageIndex() + 1,
|
|
this.handlerPages.getNumPages()),
|
|
45);
|
|
drawCenteredString(
|
|
this.fontRendererObj,
|
|
recipePage,
|
|
GuiRecipe.searchField.x + GuiRecipe.searchField.w + 22,
|
|
this.prevpage.yPosition + textMiddle,
|
|
0xffffff);
|
|
} else {
|
|
final String recipePage = NEIClientUtils.translate(
|
|
"recipe.page",
|
|
this.handlerPages.getCurrentPageIndex() + 1,
|
|
this.handlerPages.getNumPages());
|
|
drawCenteredString(
|
|
this.fontRendererObj,
|
|
recipePage,
|
|
this.guiLeft + this.xSize / 2,
|
|
this.prevpage.yPosition + textMiddle,
|
|
0xffffff);
|
|
}
|
|
|
|
if (NEIClientConfig.areJEIStyleTabsVisible()) {
|
|
RenderHelper.enableGUIStandardItemLighting();
|
|
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
|
this.recipeTabs.draw(mouseX, mouseY);
|
|
RenderHelper.disableStandardItemLighting();
|
|
}
|
|
}
|
|
|
|
private boolean isHandlerTitleHovered(int mousex, int mousey) {
|
|
final String handlerTitle = this.handler.original.getRecipeName().trim();
|
|
final int titleWidth = this.fontRendererObj.getStringWidth(handlerTitle);
|
|
final int textMiddle = (BUTTON_WIDTH - this.fontRendererObj.FONT_HEIGHT) / 2;
|
|
final int titleX = this.guiLeft + (this.xSize - titleWidth) / 2;
|
|
final int titleY = this.prevtype.yPosition + textMiddle;
|
|
return new Rectangle(titleX, titleY, titleWidth, this.fontRendererObj.FONT_HEIGHT).contains(mousex, mousey);
|
|
}
|
|
|
|
private String getHandlerTitleColorCode(boolean hovered) {
|
|
final String key = hovered ? "recipe.title.color.hover" : "recipe.title.color.normal";
|
|
final String translated = NEIClientUtils.translate(key);
|
|
if (!translated.startsWith("nei.")) { // Optional localization string for resource packs
|
|
return translated;
|
|
}
|
|
return hovered ? EnumChatFormatting.YELLOW.toString() : EnumChatFormatting.WHITE.toString();
|
|
}
|
|
|
|
@Override
|
|
public GuiContainer getFirstScreen() {
|
|
return this.firstGui;
|
|
}
|
|
|
|
@Override
|
|
public GuiScreen getFirstScreenGeneral() {
|
|
return this.firstGuiGeneral;
|
|
}
|
|
|
|
public Point getRecipePosition(int recipe) {
|
|
return getRefIndexPosition(getRecipeIndices().indexOf(recipe));
|
|
}
|
|
|
|
protected Point getRefIndexPosition(int refIndex) {
|
|
// Legacy recipe handlers using the height hack might use getRefIndexPosition in
|
|
// combination with guiTop/height
|
|
// to position certain elements like tooltips. Since guiTop is moved down by
|
|
// 16px during height hacking, we need
|
|
// to reduce the vertical shift here to 16px instead of 32px.
|
|
final List<Widget> children = this.container.getWidgets();
|
|
|
|
if (refIndex >= 0 && refIndex < children.size()
|
|
&& children.get(refIndex) instanceof NEIRecipeWidget recipeWidget) {
|
|
return new Point(
|
|
recipeWidget.x - this.guiLeft,
|
|
recipeWidget.y - this.guiTop + recipeWidget.getHandlerInfo().getYShift());
|
|
}
|
|
|
|
return new Point(0, 0);
|
|
}
|
|
|
|
public abstract ArrayList<H> getCurrentRecipeHandlers();
|
|
|
|
@Override
|
|
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
|
|
return currentVisibility;
|
|
}
|
|
|
|
@Override
|
|
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
|
|
return Collections.emptyList();
|
|
}
|
|
|
|
@Override
|
|
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
|
|
|
|
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.contains(mousex, mousey)) {
|
|
GuiRecipe.searchField.setText(SearchField.getEscapedSearchText(draggedStack));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
|
|
final Rectangle4i rect = new Rectangle4i(x, y, w, h);
|
|
// Because some of the handlers *cough avaritia* are oversized
|
|
|
|
if (this.recipeCatalyst.isShowWidget() && this.recipeCatalyst.boundsOutside().intersects(rect)) {
|
|
return true;
|
|
}
|
|
|
|
return this.container.boundsOutside().intersects(rect);
|
|
}
|
|
|
|
protected static RecipeId getCurrentRecipeId(GuiScreen gui) {
|
|
|
|
if (gui instanceof GuiRecipe<?>gRecipe && gRecipe.handlerPages.getNumRecipes() > 0) {
|
|
final List<Integer> indices = gRecipe.getRecipeIndices();
|
|
final int curRecipe = indices.isEmpty() ? 0 : indices.get(0);
|
|
final Recipe recipe = Recipe.of(gRecipe.handler.original, curRecipe);
|
|
|
|
return recipe != null ? recipe.getRecipeId() : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// some mods reflect this property
|
|
@Deprecated
|
|
private GuiOverlayButton[] overlayButtons = new GuiOverlayButton[0];
|
|
|
|
@Deprecated
|
|
public ContainerRecipe slotcontainer;
|
|
|
|
@Deprecated
|
|
public List<GuiButton> getOverlayButtons() {
|
|
return Arrays.asList(this.overlayButtons);
|
|
}
|
|
|
|
@Deprecated
|
|
public boolean isLimitedToOneRecipe() {
|
|
return false;
|
|
}
|
|
}
|