From 5a2c15ec5fc65864db7bdb9461f0445883ad14f0 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 12 Mar 2026 21:23:53 -0400 Subject: [PATCH 01/48] Implement collapsible groups --- src/main/java/mezz/jei/Internal.java | 15 ++ src/main/java/mezz/jei/config/Config.java | 8 + .../java/mezz/jei/config/ConfigValues.java | 3 + .../gui/overlay/IIngredientGridSource.java | 17 ++ .../mezz/jei/gui/overlay/IngredientGrid.java | 42 ++++- .../overlay/IngredientGridWithNavigation.java | 16 +- .../gui/overlay/IngredientListOverlay.java | 1 + .../mezz/jei/ingredients/CollapsedStack.java | 59 ++++++ .../jei/ingredients/CollapsibleEntry.java | 60 ++++++ .../ingredients/CollapsibleEntryRegistry.java | 66 +++++++ .../jei/ingredients/IngredientFilter.java | 97 +++++++++- .../jei/render/CollapsedStackRenderer.java | 178 ++++++++++++++++++ .../render/IngredientListBatchRenderer.java | 173 +++++++++++++++++ .../java/mezz/jei/startup/JeiStarter.java | 22 +++ src/main/resources/assets/jei/lang/en_us.lang | 4 + 15 files changed, 746 insertions(+), 15 deletions(-) create mode 100644 src/main/java/mezz/jei/ingredients/CollapsedStack.java create mode 100644 src/main/java/mezz/jei/ingredients/CollapsibleEntry.java create mode 100644 src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java create mode 100644 src/main/java/mezz/jei/render/CollapsedStackRenderer.java diff --git a/src/main/java/mezz/jei/Internal.java b/src/main/java/mezz/jei/Internal.java index ee87b787c..e3a37ad0d 100644 --- a/src/main/java/mezz/jei/Internal.java +++ b/src/main/java/mezz/jei/Internal.java @@ -5,6 +5,7 @@ import mezz.jei.bookmarks.BookmarkList; import mezz.jei.color.ColorNamer; import mezz.jei.gui.GuiEventHandler; +import mezz.jei.ingredients.CollapsibleEntryRegistry; import mezz.jei.ingredients.IngredientFilter; import mezz.jei.ingredients.IngredientRegistry; import mezz.jei.input.InputHandler; @@ -40,6 +41,8 @@ public final class Internal { private static InputHandler inputHandler; @Nullable private static BookmarkList bookmarkList; + @Nullable + private static CollapsibleEntryRegistry collapsibleEntryRegistry; private Internal() { @@ -151,4 +154,16 @@ public static BookmarkList getBookmarkList() { Preconditions.checkState(bookmarkList != null, "Bookmark List has not been created yet."); return bookmarkList; } + + public static CollapsibleEntryRegistry getCollapsibleEntryRegistry() { + if (collapsibleEntryRegistry == null) { + collapsibleEntryRegistry = CollapsibleEntryRegistry.getInstance(); + } + return collapsibleEntryRegistry; + } + + public static void setCollapsibleEntryRegistry(CollapsibleEntryRegistry registry) { + Internal.collapsibleEntryRegistry = registry; + CollapsibleEntryRegistry.setInstance(registry); + } } diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index de12b7b07..6ef1afd51 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -49,6 +49,7 @@ public final class Config { public static final String CATEGORY_RENDERING = "rendering"; public static final String CATEGORY_MISC = "misc"; public static final String CATEGORY_CATEGORY = "category"; + public static final String CATEGORY_COLLAPSIBLE = "collapsible"; public static final String defaultModNameFormatFriendly = "blue italic"; public static final int smallestNumColumns = 4; @@ -89,6 +90,10 @@ public static boolean isOverlayEnabled() { KeyBindings.toggleOverlay.getKeyCode() == 0; // if there is no key binding to enable it, don't allow the overlay to be disabled } + public static boolean isCollapsibleGroupsEnabled() { + return values.collapsibleGroupsEnabled; + } + public static void toggleOverlayEnabled() { values.overlayEnabled = !values.overlayEnabled; @@ -448,6 +453,7 @@ private static boolean syncConfig() { config.addCategory(CATEGORY_SEARCH); config.addCategory(CATEGORY_ADVANCED); config.addCategory(CATEGORY_MISC); + config.addCategory(CATEGORY_COLLAPSIBLE); ConfigCategory modeCategory = config.getCategory("mode"); if (modeCategory != null) { @@ -533,6 +539,8 @@ private static boolean syncConfig() { values.hideBottomLeftCornerBookmarkButton = config.getBoolean(CATEGORY_MISC, "hideBottomLeftCornerBookmarkButton", defaultValues.hideBottomLeftCornerBookmarkButton); + values.collapsibleGroupsEnabled = config.getBoolean(CATEGORY_COLLAPSIBLE, "collapsibleGroupsEnabled", defaultValues.collapsibleGroupsEnabled); + { Property property = config.get(CATEGORY_ADVANCED, "debugModeEnabled", defaultValues.debugModeEnabled); property.setShowInGui(false); diff --git a/src/main/java/mezz/jei/config/ConfigValues.java b/src/main/java/mezz/jei/config/ConfigValues.java index ab88d7a45..82cf1c0ea 100644 --- a/src/main/java/mezz/jei/config/ConfigValues.java +++ b/src/main/java/mezz/jei/config/ConfigValues.java @@ -53,4 +53,7 @@ public class ConfigValues { // category public List categoryUidOrder = new ArrayList<>(); + + // collapsible groups + public boolean collapsibleGroupsEnabled = true; } diff --git a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java index f99a4b541..2c805eee3 100644 --- a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java +++ b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java @@ -7,6 +7,23 @@ public interface IIngredientGridSource { List getIngredientList(); + /** + * Returns a list of display items for the grid, which may include + * CollapsedStack objects alongside IIngredientListElement objects + * when collapsible groups are enabled. + */ + default List getCollapsedIngredientList() { + //noinspection unchecked,rawtypes + return (List) getIngredientList(); + } + + /** + * Returns the total number of display items (counting collapsed groups as 1 each). + */ + default int collapsedSize() { + return size(); + } + int size(); void addListener(Listener listener); diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java index a8a300e81..acb8d4e75 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java @@ -6,12 +6,15 @@ import mezz.jei.gui.TooltipRenderer; import mezz.jei.gui.ingredients.GuiItemStackGroup; import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.ingredients.IngredientFilter; import mezz.jei.input.ClickedIngredient; import mezz.jei.input.IClickedIngredient; import mezz.jei.input.IShowsRecipeFocuses; import mezz.jei.input.MouseHelper; import mezz.jei.network.packets.PacketDeletePlayerItem; import mezz.jei.network.packets.PacketJei; +import mezz.jei.render.CollapsedStackRenderer; import mezz.jei.render.IngredientListBatchRenderer; import mezz.jei.render.IngredientListSlot; import mezz.jei.render.IngredientRenderer; @@ -21,6 +24,7 @@ import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -108,11 +112,17 @@ public void draw(Minecraft minecraft, int mouseX, int mouseY) { GlStateManager.disableBlend(); guiIngredientSlots.render(minecraft); + guiIngredientSlots.renderExpandedGroupOutlines(); if (!shouldDeleteItemOnClick(minecraft, mouseX, mouseY) && isMouseOver(mouseX, mouseY)) { - IngredientRenderer hovered = guiIngredientSlots.getHovered(mouseX, mouseY); - if (hovered != null) { - hovered.drawHighlight(); + CollapsedStackRenderer collapsedHovered = guiIngredientSlots.getHoveredCollapsed(mouseX, mouseY); + if (collapsedHovered != null) { + collapsedHovered.drawHighlight(); + } else { + IngredientRenderer hovered = guiIngredientSlots.getHovered(mouseX, mouseY); + if (hovered != null) { + hovered.drawHighlight(); + } } } @@ -125,9 +135,14 @@ public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { String deleteItem = Translator.translateToLocal("jei.tooltip.delete.item"); TooltipRenderer.drawHoveringText(minecraft, deleteItem, mouseX, mouseY); } else { - IngredientRenderer hovered = guiIngredientSlots.getHovered(mouseX, mouseY); - if (hovered != null) { - hovered.drawTooltip(minecraft, mouseX, mouseY); + CollapsedStackRenderer collapsedHovered = guiIngredientSlots.getHoveredCollapsed(mouseX, mouseY); + if (collapsedHovered != null) { + collapsedHovered.drawTooltip(minecraft, mouseX, mouseY); + } else { + IngredientRenderer hovered = guiIngredientSlots.getHovered(mouseX, mouseY); + if (hovered != null) { + hovered.drawTooltip(minecraft, mouseX, mouseY); + } } } } @@ -168,6 +183,21 @@ public boolean isMouseOver(int mouseX, int mouseY) { public boolean handleMouseClicked(int mouseX, int mouseY) { if (isMouseOver(mouseX, mouseY)) { + // Alt+Click toggles collapsible groups + if (GuiScreen.isAltKeyDown()) { + CollapsedStackRenderer collapsedHovered = guiIngredientSlots.getHoveredCollapsed(mouseX, mouseY); + if (collapsedHovered != null) { + collapsedHovered.getCollapsedStack().toggleExpanded(); + Internal.getIngredientFilter().notifyCollapsedStateChanged(); + return true; + } + CollapsedStack expandedHovered = guiIngredientSlots.getExpandedCollapsedGroupAt(mouseX, mouseY); + if (expandedHovered != null) { + expandedHovered.toggleExpanded(); + Internal.getIngredientFilter().notifyCollapsedStateChanged(); + return true; + } + } Minecraft minecraft = Minecraft.getMinecraft(); if (shouldDeleteItemOnClick(minecraft, mouseX, mouseY)) { EntityPlayerSP player = minecraft.player; diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java b/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java index 87ff9b263..cbb96ec3f 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java @@ -51,11 +51,11 @@ public void updateLayout(boolean resetToFirstPage) { if (resetToFirstPage) { firstItemIndex = 0; } - List ingredientList = ingredientSource.getIngredientList(); - if (firstItemIndex >= ingredientList.size()) { + List collapsedList = ingredientSource.getCollapsedIngredientList(); + if (firstItemIndex >= ingredientSource.collapsedSize()) { firstItemIndex = 0; } - this.ingredientGrid.guiIngredientSlots.set(firstItemIndex, ingredientList); + this.ingredientGrid.guiIngredientSlots.setCollapsed(firstItemIndex, collapsedList); this.navigation.updatePageState(); } @@ -200,7 +200,7 @@ public List getVisibleElements() { private class IngredientGridPaged implements IPaged { @Override public boolean nextPage() { - final int itemsCount = ingredientSource.size(); + final int itemsCount = ingredientSource.collapsedSize(); if (itemsCount > 0) { firstItemIndex += ingredientGrid.size(); if (firstItemIndex >= itemsCount) { @@ -223,7 +223,7 @@ public boolean previousPage() { updateLayout(false); return false; } - final int itemsCount = ingredientSource.size(); + final int itemsCount = ingredientSource.collapsedSize(); int pageNum = firstItemIndex / itemsPerPage; if (pageNum == 0) { @@ -245,19 +245,19 @@ public boolean previousPage() { public boolean hasNext() { // true if there is more than one page because this wraps around int itemsPerPage = ingredientGrid.size(); - return itemsPerPage > 0 && ingredientSource.size() > itemsPerPage; + return itemsPerPage > 0 && ingredientSource.collapsedSize() > itemsPerPage; } @Override public boolean hasPrevious() { // true if there is more than one page because this wraps around int itemsPerPage = ingredientGrid.size(); - return itemsPerPage > 0 && ingredientSource.size() > itemsPerPage; + return itemsPerPage > 0 && ingredientSource.collapsedSize() > itemsPerPage; } @Override public int getPageCount() { - final int itemCount = ingredientSource.size(); + final int itemCount = ingredientSource.collapsedSize(); final int stacksPerPage = ingredientGrid.size(); if (stacksPerPage == 0) { return 1; diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java b/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java index 93137d75b..2621764a6 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java @@ -59,6 +59,7 @@ public IngredientListOverlay(IngredientFilter ingredientFilter, IngredientRegist this.contents = new IngredientGridWithNavigation(ingredientFilter, guiScreenHelper, GridAlignment.LEFT); ingredientFilter.addListener(() -> onSetFilterText(Config.getFilterText())); + ingredientFilter.addCollapsedStateListener(() -> this.contents.updateLayout(false)); this.searchField = new GuiTextFieldFilter(0, ingredientFilter); this.configButton = ConfigButton.create(this); this.ghostIngredientDragManager = dragManager; diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStack.java b/src/main/java/mezz/jei/ingredients/CollapsedStack.java new file mode 100644 index 000000000..0799bc25d --- /dev/null +++ b/src/main/java/mezz/jei/ingredients/CollapsedStack.java @@ -0,0 +1,59 @@ +package mezz.jei.ingredients; + +import mezz.jei.gui.ingredients.IIngredientListElement; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a collapsed group in a specific filter result. + * Pairs a {@link CollapsibleEntry} definition with the actual matched ingredients + * from the current filtered list. + * + * This is NOT an {@link IIngredientListElement} — it coexists with them in a mixed list. + */ +public class CollapsedStack { + private final CollapsibleEntry entry; + private final List> ingredients; + + public CollapsedStack(CollapsibleEntry entry) { + this.entry = entry; + this.ingredients = new ArrayList<>(); + } + + public CollapsibleEntry getEntry() { + return entry; + } + + public List> getIngredients() { + return ingredients; + } + + public void addIngredient(IIngredientListElement element) { + ingredients.add(element); + } + + public int size() { + return ingredients.size(); + } + + public boolean isEmpty() { + return ingredients.isEmpty(); + } + + public String getDisplayName() { + return entry.getDisplayName(); + } + + public boolean isExpanded() { + return entry.isExpanded(); + } + + public void setExpanded(boolean expanded) { + entry.setExpanded(expanded); + } + + public void toggleExpanded() { + entry.toggleExpanded(); + } +} diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java new file mode 100644 index 000000000..0cf068c3c --- /dev/null +++ b/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java @@ -0,0 +1,60 @@ +package mezz.jei.ingredients; + +import mezz.jei.gui.ingredients.IIngredientListElement; +import net.minecraft.item.ItemStack; + +import java.util.function.Predicate; + +/** + * Defines a collapsible group of ingredients in the ingredient list. + * When collapsed, all matching ingredients are shown as a single entry. + * When expanded, all matching ingredients appear individually. + */ +public class CollapsibleEntry { + private final String id; + private final String displayName; + private final Predicate matcher; + private boolean expanded; + + public CollapsibleEntry(String id, String displayName, Predicate matcher) { + this.id = id; + this.displayName = displayName; + this.matcher = matcher; + this.expanded = false; + } + + public String getId() { + return id; + } + + public String getDisplayName() { + return displayName; + } + + public boolean isExpanded() { + return expanded; + } + + public void setExpanded(boolean expanded) { + this.expanded = expanded; + } + + public void toggleExpanded() { + this.expanded = !this.expanded; + } + + /** + * Tests whether the given element matches this collapsible group. + * Only ItemStack ingredients are tested; non-ItemStack ingredients never match. + */ + public boolean matches(IIngredientListElement element) { + Object ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + ItemStack itemStack = (ItemStack) ingredient; + if (!itemStack.isEmpty()) { + return matcher.test(itemStack); + } + } + return false; + } +} diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java new file mode 100644 index 000000000..adedc0bad --- /dev/null +++ b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java @@ -0,0 +1,66 @@ +package mezz.jei.ingredients; + +import net.minecraft.item.ItemStack; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Predicate; + +/** + * Registry holding all collapsible entry group definitions. + * Groups are checked in registration order; first match wins. + */ +public class CollapsibleEntryRegistry { + @Nullable + private static CollapsibleEntryRegistry instance; + + private final LinkedHashMap entries = new LinkedHashMap<>(); + private final Set disabledGroups = new HashSet<>(); + + public static CollapsibleEntryRegistry getInstance() { + if (instance == null) { + instance = new CollapsibleEntryRegistry(); + } + return instance; + } + + public static void setInstance(@Nullable CollapsibleEntryRegistry registry) { + instance = registry; + } + + /** + * Register a collapsible group with a predicate matcher. + * @param id unique identifier for the group + * @param displayName localized display name + * @param matcher predicate that returns true for ItemStacks belonging to this group + */ + public void group(String id, String displayName, Predicate matcher) { + entries.put(id, new CollapsibleEntry(id, displayName, matcher)); + } + + public Collection getEntries() { + return entries.values(); + } + + @Nullable + public CollapsibleEntry getEntry(String id) { + return entries.get(id); + } + + public void clear() { + entries.clear(); + } + + public Set getDisabledGroups() { + return disabledGroups; + } + + public void setDisabledGroups(Collection disabled) { + this.disabledGroups.clear(); + this.disabledGroups.addAll(disabled); + } + + public boolean isGroupEnabled(String id) { + return !disabledGroups.contains(id); + } +} diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index b806bed68..392f4e13b 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -31,10 +31,12 @@ public class IngredientFilter implements IIngredientFilter, IIngredientGridSourc public static boolean rebuild = false; private final List listeners = new ArrayList<>(); + private final List collapsedStateListeners = new ArrayList<>(); private IngredientBlacklistInternal blacklist; private IElementSearch elementSearch; private List ingredientListCached = Collections.emptyList(); + private List collapsedListCached = Collections.emptyList(); @Nullable private String filterCached; private boolean afterBlock = false; @@ -170,11 +172,33 @@ public List getIngredientList(String filterText) { if (!filterText.equals(filterCached)) { List> ingredientList = getIngredientListUncached(filterText); ingredientListCached = Collections.unmodifiableList(ingredientList); + collapsedListCached = collapse(ingredientListCached); filterCached = filterText; } return ingredientListCached; } + @Override + public List getCollapsedIngredientList() { + getIngredientList(); // ensure cache is populated + return collapsedListCached; + } + + @Override + public int collapsedSize() { + List collapsed = getCollapsedIngredientList(); + int count = 0; + for (Object obj : collapsed) { + if (obj instanceof CollapsedStack) { + CollapsedStack cs = (CollapsedStack) obj; + count += cs.isExpanded() ? cs.size() : 1; + } else { + count++; + } + } + return count; + } + @Override public ImmutableList getFilteredIngredients() { return getFilteredIngredients(Config.getFilterText()); @@ -228,6 +252,62 @@ private List> getIngredientListUncached(String filterT .collect(Collectors.toList()); } + /** + * Converts a flat filtered ingredient list into a mixed list containing + * both individual IIngredientListElement objects and CollapsedStack groups. + * Each ingredient is assigned to the first matching CollapsibleEntry (first match wins). + * If collapsible groups are disabled, returns the original list cast to List<Object>. + */ + private List collapse(List ingredientList) { + if (!Config.isCollapsibleGroupsEnabled()) { + return new ArrayList<>(ingredientList); + } + CollapsibleEntryRegistry registry = CollapsibleEntryRegistry.getInstance(); + Collection entries = registry.getEntries(); + if (entries.isEmpty()) { + return new ArrayList<>(ingredientList); + } + + // Build the list of active entries (not disabled) + List activeEntries = new ArrayList<>(); + for (CollapsibleEntry entry : entries) { + if (registry.isGroupEnabled(entry.getId())) { + activeEntries.add(entry); + } + } + if (activeEntries.isEmpty()) { + return new ArrayList<>(ingredientList); + } + + // Map from entry -> CollapsedStack (created on first match) + Map collapsedMap = new LinkedHashMap<>(); + List result = new ArrayList<>(ingredientList.size()); + + for (IIngredientListElement element : ingredientList) { + boolean matched = false; + for (CollapsibleEntry entry : activeEntries) { + if (entry.matches(element)) { + CollapsedStack collapsed = collapsedMap.get(entry); + if (collapsed == null) { + collapsed = new CollapsedStack(entry); + collapsedMap.put(entry, collapsed); + result.add(collapsed); + } + collapsed.addIngredient(element); + matched = true; + break; // first match wins + } + } + if (!matched) { + result.add(element); + } + } + + // Remove empty collapsed stacks (shouldn't happen, but be safe) + result.removeIf(obj -> obj instanceof CollapsedStack && ((CollapsedStack) obj).isEmpty()); + return result; + } + /** * Scans up and down the element list to find wildcard matches that touch the given element. */ @@ -284,11 +364,26 @@ public void addListener(IIngredientGridSource.Listener listener) { listeners.add(listener); } + public void addCollapsedStateListener(Runnable listener) { + collapsedStateListeners.add(listener); + } + + /** + * Called when a group is expanded or collapsed. Invalidates the cached collapsed list + * and notifies only collapsed-state listeners (preserves the current page position). + */ + public void notifyCollapsedStateChanged() { + this.filterCached = null; + for (Runnable listener : collapsedStateListeners) { + listener.run(); + } + } + public void replaceBlacklist(IngredientBlacklistInternal blacklist) { this.blacklist = blacklist; } - private void notifyListenersOfChange() { + public void notifyListenersOfChange() { for (IIngredientGridSource.Listener listener : listeners) { listener.onChange(); } diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java new file mode 100644 index 000000000..0185c6b59 --- /dev/null +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -0,0 +1,178 @@ +package mezz.jei.render; + +import mezz.jei.api.ingredients.IIngredientRenderer; +import mezz.jei.gui.TooltipRenderer; +import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.input.ClickedIngredient; +import mezz.jei.util.Translator; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.RenderItem; +import net.minecraft.item.ItemStack; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.client.config.GuiUtils; + +import javax.annotation.Nullable; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Renders a collapsed group as a single ingredient list slot. + * Shows the first item with a count badge indicating total group size, + * plus a semi-transparent background to distinguish it from normal items. + */ +public class CollapsedStackRenderer { + private static final int COLLAPSED_BG_COLOR = 0x33FFFFFF; + private static final int COLLAPSED_BORDER_COLOR = 0x55AAAAFF; + + private final CollapsedStack collapsedStack; + private Rectangle area = new Rectangle(0, 0, 16, 16); + private int padding; + + public CollapsedStackRenderer(CollapsedStack collapsedStack) { + this.collapsedStack = collapsedStack; + } + + public void setArea(Rectangle area) { + this.area = area; + } + + public void setPadding(int padding) { + this.padding = padding; + } + + public CollapsedStack getCollapsedStack() { + return collapsedStack; + } + + public Rectangle getArea() { + return area; + } + + /** + * Renders the collapsed group as a single slot. + * Draws a tinted background, the first item, and a count badge. + */ + public void render(Minecraft minecraft) { + List> ingredients = collapsedStack.getIngredients(); + if (ingredients.isEmpty()) { + return; + } + + int x = area.x + padding; + int y = area.y + padding; + + // Draw background tint to visually distinguish collapsed groups + GuiScreen.drawRect(x, y, x + 16, y + 16, COLLAPSED_BG_COLOR); + + // Render the first item as the representative + IIngredientListElement firstElement = ingredients.get(0); + Object ingredient = firstElement.getIngredient(); + if (ingredient instanceof ItemStack) { + ItemStack itemStack = (ItemStack) ingredient; + RenderHelper.enableGUIStandardItemLighting(); + RenderItem renderItem = minecraft.getRenderItem(); + renderItem.renderItemAndEffectIntoGUI(itemStack, x, y); + RenderHelper.disableStandardItemLighting(); + } else { + try { + renderIngredient(minecraft, x, y, firstElement); + } catch (RuntimeException | LinkageError e) { + // Silently ignore render errors for collapsed preview + } + } + + // Draw count badge in bottom-right + int count = collapsedStack.size(); + if (count > 1) { + String countStr = String.valueOf(count); + FontRenderer fontRenderer = minecraft.fontRenderer; + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + GlStateManager.disableBlend(); + + // Draw count text with shadow, right-aligned in the slot + int textWidth = fontRenderer.getStringWidth(countStr); + int textX = x + 17 - textWidth; + int textY = y + 9; + + fontRenderer.drawStringWithShadow(countStr, textX, textY, 0xFFFFFF); + + GlStateManager.enableDepth(); + } + + // Draw a subtle border to indicate this is a collapsible group + drawCollapsedBorder(x, y); + } + + private void drawCollapsedBorder(int x, int y) { + // Small triangle indicator in the top-left corner to show it's collapsible + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + GuiScreen.drawRect(x, y, x + 4, y + 1, COLLAPSED_BORDER_COLOR); + GuiScreen.drawRect(x, y, x + 1, y + 4, COLLAPSED_BORDER_COLOR); + GlStateManager.enableDepth(); + } + + public void drawHighlight() { + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + GlStateManager.colorMask(true, true, true, false); + GuiUtils.drawGradientRect(0, area.x, area.y, area.x + area.width, area.y + area.height, 0x80FFFFFF, 0x80FFFFFF); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableDepth(); + } + + public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { + List tooltip = new ArrayList<>(); + + // Group name and count + tooltip.add(TextFormatting.GOLD + collapsedStack.getDisplayName() + TextFormatting.GRAY + " (" + collapsedStack.size() + " items)"); + + // Show first few item names + List> ingredients = collapsedStack.getIngredients(); + int previewCount = Math.min(ingredients.size(), 5); + for (int i = 0; i < previewCount; i++) { + tooltip.add(TextFormatting.GRAY + " " + ingredients.get(i).getDisplayName()); + } + if (ingredients.size() > previewCount) { + tooltip.add(TextFormatting.DARK_GRAY + " ..." + (ingredients.size() - previewCount) + " more"); + } + + tooltip.add(""); + tooltip.add(TextFormatting.YELLOW + Translator.translateToLocal("jei.tooltip.collapsed.expand")); + + FontRenderer fontRenderer = minecraft.fontRenderer; + TooltipRenderer.drawHoveringText(minecraft, tooltip, mouseX, mouseY, fontRenderer); + } + + /** + * Gets a ClickedIngredient for the first item in the group, + * so that recipe lookups still work for the representative item. + */ + @Nullable + public ClickedIngredient getClickedIngredient() { + List> ingredients = collapsedStack.getIngredients(); + if (ingredients.isEmpty()) { + return null; + } + IIngredientListElement first = ingredients.get(0); + return ClickedIngredient.create(first.getIngredient(), area); + } + + public boolean isMouseOver(int mouseX, int mouseY) { + return area.contains(mouseX, mouseY); + } + + @SuppressWarnings("unchecked") + private static void renderIngredient(Minecraft minecraft, int x, int y, IIngredientListElement element) { + IIngredientRenderer renderer = element.getIngredientRenderer(); + T ingredient = element.getIngredient(); + renderer.render(minecraft, x, y, ingredient); + } +} diff --git a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java index a11eec0ef..c2ecec313 100644 --- a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java @@ -5,6 +5,7 @@ import mezz.jei.api.ingredients.ISlowRenderItem; import mezz.jei.config.Config; import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.ingredients.CollapsedStack; import mezz.jei.input.ClickedIngredient; import mezz.jei.util.ErrorUtil; import mezz.jei.util.Log; @@ -19,9 +20,15 @@ import net.minecraft.item.ItemStack; import org.lwjgl.opengl.GL11; +import net.minecraft.client.gui.Gui; + import javax.annotation.Nullable; +import java.awt.Rectangle; import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static mezz.jei.gui.overlay.IngredientGrid.INGREDIENT_HEIGHT; @@ -33,6 +40,11 @@ public class IngredientListBatchRenderer { protected final List renderItems2d = new ArrayList<>(); protected final List renderItems3d = new ArrayList<>(); protected final List renderOther = new ArrayList<>(); + protected final List renderCollapsed = new ArrayList<>(); + protected final Map collapsedStackIndexed = new HashMap<>(); + protected final Map, CollapsedStack> expandedElementToGroup = new HashMap<>(); + // Per-group list of individual slot rectangles (used for per-slot fill + edge-detection border). + protected final Map> expandedGroupSlots = new HashMap<>(); @Nullable private Framebuffer framebuffer = null; @@ -59,6 +71,10 @@ public void clear() { renderItems2d.clear(); renderItems3d.clear(); renderOther.clear(); + renderCollapsed.clear(); + collapsedStackIndexed.clear(); + expandedElementToGroup.clear(); + expandedGroupSlots.clear(); size = 0; maxSize = 0; @@ -83,6 +99,10 @@ public void set(final int startIndex, List ingredientLis renderItems2d.clear(); renderItems3d.clear(); renderOther.clear(); + renderCollapsed.clear(); + collapsedStackIndexed.clear(); + expandedElementToGroup.clear(); + expandedGroupSlots.clear(); maxSize = 0; size = 0; @@ -114,6 +134,91 @@ public void set(final int startIndex, List ingredientLis invalidateBuffer(); } + /** + * Sets the grid contents from a collapsed ingredient list (mixed IIngredientListElement and CollapsedStack objects). + * Collapsed groups are rendered as a single slot; expanded groups have their items rendered individually. + */ + public void setCollapsed(final int startIndex, List collapsedList) { + renderItems2d.clear(); + renderItems3d.clear(); + renderOther.clear(); + renderCollapsed.clear(); + collapsedStackIndexed.clear(); + expandedElementToGroup.clear(); + expandedGroupSlots.clear(); + maxSize = 0; + size = 0; + + for (List row : slots) { + for (IngredientListSlot slot : row) { + slot.clear(); + } + } + + // Flatten the ENTIRE collapsed list into display items first, then slice at startIndex. + // This ensures expanded groups don't break pagination — firstItemIndex is an index into + // the flattened view, which matches what collapsedSize() now returns. + List displayItems = new ArrayList<>(); + Map itemToCollapsed = new HashMap<>(); + for (Object obj : collapsedList) { + if (obj instanceof CollapsedStack) { + CollapsedStack collapsed = (CollapsedStack) obj; + if (collapsed.isExpanded()) { + // Expanded: add each ingredient individually, track which belong to this group + for (IIngredientListElement element : collapsed.getIngredients()) { + displayItems.add(element); + itemToCollapsed.put(element, collapsed); + } + } else { + // Collapsed: add the CollapsedStack itself as a single display item + displayItems.add(collapsed); + } + } else { + displayItems.add(obj); + } + } + + int i = startIndex; + int slotIndex = 0; + for (List row : slots) { + maxSize += (int) row.stream().filter(IngredientListSlot::isFree).count(); + for (int column = 0; column < row.size(); column++) { + if (i >= displayItems.size()) { + break; + } + IngredientListSlot ingredientListSlot = row.get(column); + if (ingredientListSlot.isBlocked()) { + slotIndex++; + continue; + } + Object displayItem = displayItems.get(i); + if (displayItem instanceof CollapsedStack) { + CollapsedStack collapsed = (CollapsedStack) displayItem; + CollapsedStackRenderer renderer = new CollapsedStackRenderer(collapsed); + renderer.setArea(ingredientListSlot.getArea()); + renderer.setPadding(1); + renderCollapsed.add(renderer); + collapsedStackIndexed.put(slotIndex, collapsed); + } else if (displayItem instanceof IIngredientListElement) { + IIngredientListElement element = (IIngredientListElement) displayItem; + set(ingredientListSlot, element); + CollapsedStack parentCollapsed = itemToCollapsed.get(element); + if (parentCollapsed != null) { + collapsedStackIndexed.put(slotIndex, parentCollapsed); + expandedElementToGroup.put(element, parentCollapsed); + expandedGroupSlots.computeIfAbsent(parentCollapsed, k -> new ArrayList<>()) + .add(new Rectangle(ingredientListSlot.getArea())); + } + } + size++; + i++; + slotIndex++; + } + } + + invalidateBuffer(); + } + /** * Returns the maximum number of ingredients that can be displayed, if none of them ended rows early. * @return the maximum number of ingredients. @@ -191,6 +296,11 @@ public void moveSlotsToFit(int maxWidth) { @Nullable public ClickedIngredient getIngredientUnderMouse(int mouseX, int mouseY) { + // Check collapsed renderers first + CollapsedStackRenderer collapsedHovered = getHoveredCollapsed(mouseX, mouseY); + if (collapsedHovered != null) { + return collapsedHovered.getClickedIngredient(); + } IngredientRenderer hovered = getHovered(mouseX, mouseY); if (hovered != null) { IIngredientListElement element = hovered.getElement(); @@ -208,6 +318,64 @@ public IngredientRenderer getHovered(int mouseX, int mouseY) { return null; } + @Nullable + public CollapsedStack getExpandedCollapsedGroupAt(int mouseX, int mouseY) { + IngredientRenderer hovered = getHovered(mouseX, mouseY); + if (hovered == null) return null; + return expandedElementToGroup.get(hovered.getElement()); + } + + public void renderExpandedGroupOutlines() { + if (expandedGroupSlots.isEmpty()) return; + GlStateManager.disableLighting(); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate( + GlStateManager.SourceFactor.SRC_ALPHA, + GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, + GlStateManager.SourceFactor.ONE, + GlStateManager.DestFactor.ZERO + ); + int bgColor = 0x33555555; // subtle smoke background + int borderColor = 0xCC888888; // medium smoke border + for (List slots : expandedGroupSlots.values()) { + // Build a fast lookup set keyed by "x,y" to detect adjacent group slots. + java.util.Set keys = new java.util.HashSet<>(); + for (Rectangle r : slots) keys.add(r.x + "," + r.y); + for (Rectangle r : slots) { + // Subtle background fill over each slot + Gui.drawRect(r.x, r.y, r.x + r.width, r.y + r.height, bgColor); + // Draw only the edges that are NOT shared with another group slot + if (!keys.contains(r.x + "," + (r.y - INGREDIENT_HEIGHT))) { + Gui.drawRect(r.x, r.y, r.x + r.width, r.y + 1, borderColor); // top + } + if (!keys.contains(r.x + "," + (r.y + INGREDIENT_HEIGHT))) { + Gui.drawRect(r.x, r.y + r.height - 1, r.x + r.width, r.y + r.height, borderColor); // bottom + } + if (!keys.contains((r.x - INGREDIENT_WIDTH) + "," + r.y)) { + Gui.drawRect(r.x, r.y, r.x + 1, r.y + r.height, borderColor); // left + } + if (!keys.contains((r.x + INGREDIENT_WIDTH) + "," + r.y)) { + Gui.drawRect(r.x + r.width - 1, r.y, r.x + r.width, r.y + r.height, borderColor); // right + } + } + } + GlStateManager.disableBlend(); + } + + @Nullable + public CollapsedStackRenderer getHoveredCollapsed(int mouseX, int mouseY) { + for (CollapsedStackRenderer renderer : renderCollapsed) { + if (renderer.isMouseOver(mouseX, mouseY)) { + return renderer; + } + } + return null; + } + + public Map getCollapsedStackIndexed() { + return collapsedStackIndexed; + } + public void render(Minecraft minecraft) { if (allowBuffering && !Config.isEditModeEnabled() && Config.bufferIngredientRenders() && OpenGlHelper.isFramebufferEnabled()) { if (framebuffer == null) { @@ -310,6 +478,11 @@ protected void renderImpl(Minecraft minecraft) { slot.renderSlow(); } + // collapsed group rendering + for (CollapsedStackRenderer collapsed : renderCollapsed) { + collapsed.render(minecraft); + } + RenderHelper.disableStandardItemLighting(); } diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index 2107badee..d6253d4d2 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -12,6 +12,7 @@ import mezz.jei.bookmarks.BookmarkList; import mezz.jei.config.Config; import mezz.jei.gui.GuiEventHandler; +import mezz.jei.ingredients.CollapsibleEntryRegistry; import mezz.jei.gui.GuiHelper; import mezz.jei.gui.GuiScreenHelper; import mezz.jei.gui.ghost.GhostIngredientDragManager; @@ -33,6 +34,9 @@ import mezz.jei.util.ErrorUtil; import mezz.jei.util.Log; import mezz.jei.util.LoggedTimer; +import net.minecraft.init.Items; +import net.minecraft.item.ItemEnchantedBook; +import net.minecraft.item.ItemMonsterPlacer; import net.minecraftforge.fml.common.ProgressManager; import java.util.Iterator; @@ -97,6 +101,8 @@ public void load(List plugins, Textures textures, boolean recipesOnl timer.stop(); } + registerDefaultCollapsibleGroups(); + BookmarkList bookmarkList = new BookmarkList(ingredientRegistry); Internal.setBookmarkList(bookmarkList); @@ -336,4 +342,20 @@ private static void sendRuntime(List plugins, IJeiRuntime jeiRuntime } } + private static void registerDefaultCollapsibleGroups() { + CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + registry.group("enchanted_books", "Enchanted Books", + stack -> stack.getItem() instanceof ItemEnchantedBook); + registry.group("potions", "Potions", + stack -> stack.getItem() == Items.POTIONITEM); + registry.group("splash_potions", "Splash Potions", + stack -> stack.getItem() == Items.SPLASH_POTION); + registry.group("lingering_potions", "Lingering Potions", + stack -> stack.getItem() == Items.LINGERING_POTION); + registry.group("tipped_arrows", "Tipped Arrows", + stack -> stack.getItem() == Items.TIPPED_ARROW); + registry.group("spawn_eggs", "Spawn Eggs", + stack -> stack.getItem() instanceof ItemMonsterPlacer); + } + } diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index dc47485eb..cb5e519b4 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -22,6 +22,10 @@ jei.tooltip.bookmarks.usage.nokey=Add a key binding for HEI bookmarks in your Co jei.tooltip.bookmarks.usage.key=Hover over an ingredient and press "%s" to bookmark it. jei.tooltip.bookmarks.not.enough.space=There is not enough space to display bookmarks here. +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Click to expand +jei.tooltip.collapsed.collapse=Alt+Click to collapse + # Error Tooltips jei.tooltip.error.recipe.transfer.missing=Missing Items jei.tooltip.error.recipe.transfer.inventory.full=Inventory is too full From e7fe24c469a01e148d2dd359b15a34c60d140f14 Mon Sep 17 00:00:00 2001 From: Tommy Date: Thu, 12 Mar 2026 22:04:29 -0400 Subject: [PATCH 02/48] Fix options, minor changes. Fix the option being placed in the HEI menu. Placement under hide mode but likely will be moved later. --- .../java/mezz/jei/config/JEIModConfigGui.java | 24 +++++++++++++++++-- src/main/resources/assets/jei/lang/en_us.lang | 5 ++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/mezz/jei/config/JEIModConfigGui.java b/src/main/java/mezz/jei/config/JEIModConfigGui.java index f65dd89f3..438ff86dd 100644 --- a/src/main/java/mezz/jei/config/JEIModConfigGui.java +++ b/src/main/java/mezz/jei/config/JEIModConfigGui.java @@ -52,16 +52,31 @@ private static GuiScreen getParent(GuiScreen parent) { private static List getConfigElements() { List configElements = new ArrayList<>(); + LocalizedConfiguration config = Config.getConfig(); + if (Minecraft.getMinecraft().world != null) { Configuration worldConfig = Config.getWorldConfig(); if (worldConfig != null) { NetworkManager networkManager = FMLClientHandler.instance().getClientToServerNetworkManager(); ConfigCategory categoryWorldConfig = worldConfig.getCategory(ServerInfo.getWorldUid(networkManager)); - configElements.addAll(new ConfigElement(categoryWorldConfig).getChildElements()); + List worldElements = new ConfigElement(categoryWorldConfig).getChildElements(); + + // Find the "Hide Ingredients Mode" entry and insert Collapsible Groups submenu immediately after it + int insertAt = worldElements.size(); + for (int i = 0; i < worldElements.size(); i++) { + if ("config.jei.mode.editEnabled".equals(worldElements.get(i).getLanguageKey())) { + insertAt = i + 1; + break; + } + } + configElements.addAll(worldElements.subList(0, insertAt)); + if (config != null) { + configElements.add(new ConfigElement(config.getCategory(Config.CATEGORY_COLLAPSIBLE))); + } + configElements.addAll(worldElements.subList(insertAt, worldElements.size())); } } - LocalizedConfiguration config = Config.getConfig(); if (config != null) { ConfigCategory categoryAdvanced = config.getCategory(Config.CATEGORY_ADVANCED); configElements.addAll(new ConfigElement(categoryAdvanced).getChildElements()); @@ -72,6 +87,11 @@ private static List getConfigElements() { ConfigCategory categoryMisc = config.getCategory(Config.CATEGORY_MISC); configElements.addAll(new ConfigElement(categoryMisc).getChildElements()); + // If we never had a world config section (world == null), show Collapsible Groups here instead + if (Minecraft.getMinecraft().world == null) { + configElements.add(new ConfigElement(config.getCategory(Config.CATEGORY_COLLAPSIBLE))); + } + ConfigCategory categorySearch = config.getCategory(Config.CATEGORY_SEARCH); configElements.add(new ConfigElement(categorySearch)); } diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index cb5e519b4..5d86e0dc2 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -186,6 +186,11 @@ config.jei.misc.hideBottomRightCornerConfigButton.comment=Whether to hide the bo config.jei.misc.hideBottomLeftCornerBookmarkButton=Hide Bottom-Left Corner Bookmark Button config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Whether to hide the bottom-left corner bookmark button +config.jei.collapsible=Collapsible Groups +config.jei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. +config.jei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups +config.jei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. + config.jei.category.categoryUidOrder=Category UID Order config.jei.category.categoryUidOrder.comment=Determines the display order of recipe categories in HEI. Categories listed earlier appear first in the HEI interface. From 6ba8779caf566bb8f0a3e2b99a46f7f3ab6a8f2a Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 01:06:18 -0400 Subject: [PATCH 03/48] Implement collapsible group settings Add a group management screen to create and edit custom groups and toggle built in groups. --- src/main/java/mezz/jei/config/Config.java | 42 +- .../java/mezz/jei/config/ConfigValues.java | 3 + .../mezz/jei/config/CustomGroupsConfig.java | 103 ++++ .../java/mezz/jei/config/JEIModConfigGui.java | 74 ++- .../collapsible/GuiCollapsibleGroups.java | 431 ++++++++++++++ .../collapsible/GuiCustomGroupEditor.java | 556 ++++++++++++++++++ .../ingredients/CollapsibleEntryRegistry.java | 56 ++ .../jei/ingredients/IngredientFilter.java | 8 +- .../java/mezz/jei/startup/JeiStarter.java | 6 + src/main/resources/assets/jei/lang/en_us.lang | 13 + 10 files changed, 1288 insertions(+), 4 deletions(-) create mode 100644 src/main/java/mezz/jei/config/CustomGroupsConfig.java create mode 100644 src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java create mode 100644 src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index 6ef1afd51..601638482 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -66,6 +66,8 @@ public final class Config { @Nullable private static LocalizedConfiguration searchColorsConfig; @Nullable + private static CustomGroupsConfig customGroupsConfig; + @Nullable private static File bookmarkFile; @Nullable private static File favoriteFile; @@ -94,6 +96,27 @@ public static boolean isCollapsibleGroupsEnabled() { return values.collapsibleGroupsEnabled; } + @Nullable + public static CustomGroupsConfig getCustomGroupsConfig() { + return customGroupsConfig; + } + + public static Set getDisabledGroups() { + return values.disabledGroups; + } + + public static void saveDisabledGroups(Set disabledGroups) { + values.disabledGroups.clear(); + values.disabledGroups.addAll(disabledGroups); + if (config != null) { + Property property = config.get(CATEGORY_COLLAPSIBLE, "disabledGroups", new String[]{}); + property.set(disabledGroups.toArray(new String[0])); + if (config.hasChanged()) { + config.save(); + } + } + } + public static void toggleOverlayEnabled() { values.overlayEnabled = !values.overlayEnabled; @@ -417,6 +440,9 @@ public static void preInit(FMLPreInitializationEvent event) { itemBlacklistConfig = new LocalizedConfiguration(configKeyPrefix, itemBlacklistConfigFile, "0.1.0"); searchColorsConfig = new LocalizedConfiguration(configKeyPrefix, searchColorsConfigFile, "0.1.0"); + customGroupsConfig = new CustomGroupsConfig(jeiConfigurationDir); + customGroupsConfig.load(); + syncConfig(); syncItemBlacklistConfig(); syncSearchColorsConfig(); @@ -539,7 +565,21 @@ private static boolean syncConfig() { values.hideBottomLeftCornerBookmarkButton = config.getBoolean(CATEGORY_MISC, "hideBottomLeftCornerBookmarkButton", defaultValues.hideBottomLeftCornerBookmarkButton); - values.collapsibleGroupsEnabled = config.getBoolean(CATEGORY_COLLAPSIBLE, "collapsibleGroupsEnabled", defaultValues.collapsibleGroupsEnabled); + { + boolean prev = values.collapsibleGroupsEnabled; + values.collapsibleGroupsEnabled = config.getBoolean(CATEGORY_COLLAPSIBLE, "collapsibleGroupsEnabled", defaultValues.collapsibleGroupsEnabled); + if (prev != values.collapsibleGroupsEnabled) { + needsReload = true; + } + } + + { + String[] disabledGroupsArray = config.getStringList("disabledGroups", CATEGORY_COLLAPSIBLE, new String[]{}); + Property disabledProp = config.get(CATEGORY_COLLAPSIBLE, "disabledGroups", new String[]{}); + disabledProp.setShowInGui(false); + values.disabledGroups.clear(); + Collections.addAll(values.disabledGroups, disabledGroupsArray); + } { Property property = config.get(CATEGORY_ADVANCED, "debugModeEnabled", defaultValues.debugModeEnabled); diff --git a/src/main/java/mezz/jei/config/ConfigValues.java b/src/main/java/mezz/jei/config/ConfigValues.java index 82cf1c0ea..2eb04c70a 100644 --- a/src/main/java/mezz/jei/config/ConfigValues.java +++ b/src/main/java/mezz/jei/config/ConfigValues.java @@ -5,7 +5,9 @@ import net.minecraft.item.ItemStack; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ConfigValues { // advanced @@ -56,4 +58,5 @@ public class ConfigValues { // collapsible groups public boolean collapsibleGroupsEnabled = true; + public Set disabledGroups = new HashSet<>(); } diff --git a/src/main/java/mezz/jei/config/CustomGroupsConfig.java b/src/main/java/mezz/jei/config/CustomGroupsConfig.java new file mode 100644 index 000000000..a67ae65df --- /dev/null +++ b/src/main/java/mezz/jei/config/CustomGroupsConfig.java @@ -0,0 +1,103 @@ +package mezz.jei.config; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import mezz.jei.util.Log; + +import javax.annotation.Nullable; +import java.io.*; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Manages custom collapsible groups persistence as JSON. + * File: config/jei/customCollapsibleGroups.json + */ +public class CustomGroupsConfig { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Type GROUP_LIST_TYPE = new TypeToken>() {}.getType(); + + private final File configFile; + private List customGroups = new ArrayList<>(); + + public CustomGroupsConfig(File configDir) { + this.configFile = new File(configDir, "customCollapsibleGroups.json"); + } + + public void load() { + if (!configFile.exists()) { + customGroups = new ArrayList<>(); + return; + } + try (Reader reader = new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8)) { + List loaded = GSON.fromJson(reader, GROUP_LIST_TYPE); + customGroups = loaded != null ? loaded : new ArrayList<>(); + } catch (Exception e) { + Log.get().error("Failed to load custom collapsible groups from {}", configFile, e); + customGroups = new ArrayList<>(); + } + } + + public void save() { + try (Writer writer = new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8)) { + GSON.toJson(customGroups, GROUP_LIST_TYPE, writer); + } catch (Exception e) { + Log.get().error("Failed to save custom collapsible groups to {}", configFile, e); + } + } + + public List getCustomGroups() { + return customGroups; + } + + public void addGroup(CustomGroup group) { + customGroups.add(group); + save(); + } + + public void removeGroup(String id) { + customGroups.removeIf(g -> g.id.equals(id)); + save(); + } + + public void updateGroup(CustomGroup updated) { + for (int i = 0; i < customGroups.size(); i++) { + if (customGroups.get(i).id.equals(updated.id)) { + customGroups.set(i, updated); + save(); + return; + } + } + // Not found — add as new + addGroup(updated); + } + + /** + * A user-defined collapsible group stored as JSON. + * Items are identified by their unique identifier string from StackHelper. + */ + public static class CustomGroup { + public String id; + public String displayName; + public List itemUids; + + public CustomGroup() { + this.id = ""; + this.displayName = ""; + this.itemUids = new ArrayList<>(); + } + + public CustomGroup(String id, String displayName, List itemUids) { + this.id = id; + this.displayName = displayName; + this.itemUids = new ArrayList<>(itemUids); + } + + public CustomGroup copy() { + return new CustomGroup(id, displayName, new ArrayList<>(itemUids)); + } + } +} diff --git a/src/main/java/mezz/jei/config/JEIModConfigGui.java b/src/main/java/mezz/jei/config/JEIModConfigGui.java index 438ff86dd..69cef12ce 100644 --- a/src/main/java/mezz/jei/config/JEIModConfigGui.java +++ b/src/main/java/mezz/jei/config/JEIModConfigGui.java @@ -1,11 +1,15 @@ package mezz.jei.config; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.regex.Pattern; import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.client.GuiModList; +import net.minecraftforge.fml.client.config.ConfigGuiType; import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.client.config.GuiConfigEntries; import net.minecraftforge.fml.client.config.IConfigElement; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.ConfigElement; @@ -18,6 +22,7 @@ import net.minecraft.network.NetworkManager; import mezz.jei.JustEnoughItems; +import mezz.jei.gui.overlay.collapsible.GuiCollapsibleGroups; import mezz.jei.gui.recipes.RecipesGui; import mezz.jei.network.packets.PacketRequestCheatPermission; import mezz.jei.util.Translator; @@ -49,6 +54,18 @@ private static GuiScreen getParent(GuiScreen parent) { return parent; } + private static void addCollapsibleElements(List configElements, LocalizedConfiguration config) { + // Show collapsibleGroupsEnabled inline (aligned like other boolean properties) + ConfigCategory catCollapsible = config.getCategory(Config.CATEGORY_COLLAPSIBLE); + for (IConfigElement element : new ConfigElement(catCollapsible).getChildElements()) { + if (element.showInGui()) { + configElements.add(element); + } + } + // "Manage Groups" navigation entry — opens GuiCollapsibleGroups in the same visual row style + configElements.add(new ManageGroupsConfigElement()); + } + private static List getConfigElements() { List configElements = new ArrayList<>(); @@ -71,7 +88,7 @@ private static List getConfigElements() { } configElements.addAll(worldElements.subList(0, insertAt)); if (config != null) { - configElements.add(new ConfigElement(config.getCategory(Config.CATEGORY_COLLAPSIBLE))); + addCollapsibleElements(configElements, config); } configElements.addAll(worldElements.subList(insertAt, worldElements.size())); } @@ -89,7 +106,7 @@ private static List getConfigElements() { // If we never had a world config section (world == null), show Collapsible Groups here instead if (Minecraft.getMinecraft().world == null) { - configElements.add(new ConfigElement(config.getCategory(Config.CATEGORY_COLLAPSIBLE))); + addCollapsibleElements(configElements, config); } ConfigCategory categorySearch = config.getCategory(Config.CATEGORY_SEARCH); @@ -117,4 +134,57 @@ protected void actionPerformed(GuiButton button) { JustEnoughItems.getProxy().sendPacketToServer(new PacketRequestCheatPermission()); } } + + // ------------------------------------------------------------------------- + // "Manage Groups" config list entry + // ------------------------------------------------------------------------- + + /** + * A CategoryEntry that opens GuiCollapsibleGroups instead of a standard GuiConfig subcategory. + * Constructor signature must match (GuiConfig, GuiConfigEntries, IConfigElement). + */ + public static class ManageGroupsEntry extends GuiConfigEntries.CategoryEntry { + public ManageGroupsEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement configElement) { + super(owningScreen, owningEntryList, configElement); + } + + @Override + protected GuiScreen buildChildScreen() { + return new GuiCollapsibleGroups(owningScreen); + } + } + + /** + * A minimal IConfigElement that represents a category-type navigation entry + * pointing to GuiCollapsibleGroups via ManageGroupsEntry. + */ + public static class ManageGroupsConfigElement implements IConfigElement { + @Override public boolean isProperty() { return false; } + @Override public Class getConfigEntryClass() { return ManageGroupsEntry.class; } + @Override public Class getArrayEntryClass() { return null; } + @Override public String getName() { return "manageGroups"; } + @Override public String getQualifiedName() { return "manageGroups"; } + @Override public String getLanguageKey() { return "jei.gui.collapsible.title"; } + @Override public String getComment() { return ""; } + @Override public List getChildElements() { return Collections.emptyList(); } + @Override public ConfigGuiType getType() { return ConfigGuiType.CONFIG_CATEGORY; } + @Override public boolean isList() { return false; } + @Override public boolean isListLengthFixed() { return false; } + @Override public int getMaxListLength() { return -1; } + @Override public boolean isDefault() { return true; } + @Override public Object getDefault() { return null; } + @Override public Object[] getDefaults() { return null; } + @Override public void setToDefault() {} + @Override public boolean requiresWorldRestart() { return false; } + @Override public boolean showInGui() { return true; } + @Override public boolean requiresMcRestart() { return false; } + @Override public Object get() { return null; } + @Override public Object[] getList() { return null; } + @Override public void set(Object value) {} + @Override public void set(Object[] aVal) {} + @Override public String[] getValidValues() { return null; } + @Override public Object getMinValue() { return null; } + @Override public Object getMaxValue() { return null; } + @Override public Pattern getValidationPattern() { return null; } + } } diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java new file mode 100644 index 000000000..66e30eee2 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -0,0 +1,431 @@ +package mezz.jei.gui.overlay.collapsible; + +import mezz.jei.Internal; +import mezz.jei.config.Config; +import mezz.jei.config.CustomGroupsConfig; +import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.ingredients.CollapsibleEntry; +import mezz.jei.ingredients.CollapsibleEntryRegistry; +import mezz.jei.ingredients.IngredientFilter; +import mezz.jei.util.Translator; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.client.config.GuiUtils; +import org.lwjgl.input.Mouse; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.*; + +/** + * Management screen for collapsible groups. + * Shows all default and custom groups with toggle/configure/delete controls. + */ +public class GuiCollapsibleGroups extends GuiScreen { + + private static final int CARD_HEIGHT = 60; + private static final int CARD_PADDING = 4; + private static final int PREVIEW_SIZE = 16; + private static final int PREVIEW_COLS = 8; + private static final int CARDS_PER_COL = 3; + private static final int CARDS_PER_PAGE = 6; // 2 columns × 3 rows + + private static final int BTN_BACK = 0; + private static final int BTN_NEW = 1; + private static final int BTN_PREV_PAGE = 2; + private static final int BTN_NEXT_PAGE = 3; + private static final int BTN_TOGGLE_BASE = 100; + private static final int BTN_CONFIGURE_BASE = 200; + private static final int BTN_DELETE_BASE = 300; + + private final GuiScreen parentScreen; + private final List cardEntries = new ArrayList<>(); + private int currentPage = 0; + private int totalPages = 1; + + public GuiCollapsibleGroups(GuiScreen parentScreen) { + this.parentScreen = parentScreen; + } + + @Override + public void initGui() { + super.initGui(); + this.buttonList.clear(); + + // Back button + this.buttonList.add(new GuiButton(BTN_BACK, 4, 4, 60, 20, + Translator.translateToLocal("jei.gui.collapsible.back"))); + + // New Group button + this.buttonList.add(new GuiButton(BTN_NEW, this.width - 64, 4, 60, 20, + "+ " + Translator.translateToLocal("jei.gui.collapsible.newGroup"))); + + rebuildCards(); + rebuildPageButtons(); + } + + private void rebuildCards() { + cardEntries.clear(); + + CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + + // Custom groups come first (like REI) + for (CollapsibleEntry entry : registry.getCustomEntries()) { + List previewItems = getPreviewItems(entry); + int itemCount = getMatchedItemCount(entry); + cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), true, + !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); + } + + // Default groups + for (CollapsibleEntry entry : registry.getEntries()) { + List previewItems = getPreviewItems(entry); + int itemCount = getMatchedItemCount(entry); + cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), false, + !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); + } + + totalPages = Math.max(1, (cardEntries.size() + CARDS_PER_PAGE - 1) / CARDS_PER_PAGE); + if (currentPage >= totalPages) { + currentPage = totalPages - 1; + } + } + + private void rebuildPageButtons() { + // Remove old card-specific and page buttons + buttonList.removeIf(b -> b.id >= BTN_PREV_PAGE); + + int contentTop = 30; + int contentWidth = Math.min(this.width - 10, 480); + int contentLeft = (this.width - contentWidth) / 2; + int colGap = 4; + int colWidth = (contentWidth - colGap) / 2; + + int startIdx = currentPage * CARDS_PER_PAGE; + int endIdx = Math.min(startIdx + CARDS_PER_PAGE, cardEntries.size()); + + for (int i = startIdx; i < endIdx; i++) { + int localIdx = i - startIdx; + int col = localIdx / CARDS_PER_COL; // 0 = left, 1 = right + int row = localIdx % CARDS_PER_COL; + int cardX = contentLeft + col * (colWidth + colGap); + int cardY = contentTop + row * (CARD_HEIGHT + CARD_PADDING); + GroupCardEntry card = cardEntries.get(i); + + int btnX = cardX + colWidth - 56; + int btnY = cardY + 4; + + // Toggle button + String toggleLabel = card.enabled + ? Translator.translateToLocal("jei.gui.collapsible.enabled") + : Translator.translateToLocal("jei.gui.collapsible.disabled"); + this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 14, toggleLabel)); + + if (card.isCustom) { + // Configure button + this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 16, 24, 14, + "\u270E")); // pencil unicode + // Delete button + this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 16, 26, 14, + "\u2716")); // cross unicode + } + } + + // Page navigation + if (totalPages > 1) { + int navY = contentTop + CARDS_PER_COL * (CARD_HEIGHT + CARD_PADDING) + 4; + this.buttonList.add(new GuiButton(BTN_PREV_PAGE, contentLeft, navY, 40, 20, "<")); + this.buttonList.add(new GuiButton(BTN_NEXT_PAGE, contentLeft + contentWidth - 40, navY, 40, 20, ">")); + } + } + + @Override + protected void actionPerformed(GuiButton button) throws IOException { + if (button.id == BTN_BACK) { + this.mc.displayGuiScreen(parentScreen); + return; + } + if (button.id == BTN_NEW) { + String newId = "custom:" + UUID.randomUUID().toString().substring(0, 8); + CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); + if (customGroupsConfig != null) { + CustomGroupsConfig.CustomGroup newGroup = new CustomGroupsConfig.CustomGroup(newId, "New Group", new ArrayList<>()); + customGroupsConfig.addGroup(newGroup); + Internal.getCollapsibleEntryRegistry().recollectCustomEntries(); + this.mc.displayGuiScreen(new GuiCustomGroupEditor(this, newGroup)); + } + return; + } + if (button.id == BTN_PREV_PAGE) { + currentPage = Math.max(0, currentPage - 1); + rebuildPageButtons(); + return; + } + if (button.id == BTN_NEXT_PAGE) { + currentPage = Math.min(totalPages - 1, currentPage + 1); + rebuildPageButtons(); + return; + } + + // Toggle + if (button.id >= BTN_TOGGLE_BASE && button.id < BTN_CONFIGURE_BASE) { + int idx = button.id - BTN_TOGGLE_BASE; + if (idx >= 0 && idx < cardEntries.size()) { + GroupCardEntry card = cardEntries.get(idx); + card.enabled = !card.enabled; + + CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + Set disabled = new HashSet<>(registry.getDisabledGroups()); + if (card.enabled) { + disabled.remove(card.id); + } else { + disabled.add(card.id); + } + registry.setDisabledGroups(disabled); + Config.saveDisabledGroups(disabled); + + if (Internal.hasIngredientFilter()) { + IngredientFilter filter = Internal.getIngredientFilter(); + filter.invalidateCache(); + filter.getIngredientList(Config.getFilterText()); + filter.notifyListenersOfChange(); + } + + rebuildPageButtons(); + } + return; + } + + // Configure + if (button.id >= BTN_CONFIGURE_BASE && button.id < BTN_DELETE_BASE) { + int idx = button.id - BTN_CONFIGURE_BASE; + if (idx >= 0 && idx < cardEntries.size()) { + GroupCardEntry card = cardEntries.get(idx); + if (card.isCustom) { + CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); + if (customGroupsConfig != null) { + for (CustomGroupsConfig.CustomGroup group : customGroupsConfig.getCustomGroups()) { + if (group.id.equals(card.id)) { + this.mc.displayGuiScreen(new GuiCustomGroupEditor(this, group)); + return; + } + } + } + } + } + return; + } + + // Delete + if (button.id >= BTN_DELETE_BASE) { + int idx = button.id - BTN_DELETE_BASE; + if (idx >= 0 && idx < cardEntries.size()) { + GroupCardEntry card = cardEntries.get(idx); + if (card.isCustom) { + CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); + if (customGroupsConfig != null) { + customGroupsConfig.removeGroup(card.id); + Internal.getCollapsibleEntryRegistry().recollectCustomEntries(); + + if (Internal.hasIngredientFilter()) { + IngredientFilter filter = Internal.getIngredientFilter(); + filter.invalidateCache(); + filter.getIngredientList(Config.getFilterText()); + filter.notifyListenersOfChange(); + } + + rebuildCards(); + rebuildPageButtons(); + } + } + } + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + this.drawDefaultBackground(); + + // Title + String title = Translator.translateToLocal("jei.gui.collapsible.title"); + this.drawCenteredString(this.fontRenderer, title, this.width / 2, 10, 0xFFFFFF); + + int contentWidth = Math.min(this.width - 10, 480); + int contentLeft = (this.width - contentWidth) / 2; + int contentTop = 30; + int colGap = 4; + int colWidth = (contentWidth - colGap) / 2; + + int startIdx = currentPage * CARDS_PER_PAGE; + int endIdx = Math.min(startIdx + CARDS_PER_PAGE, cardEntries.size()); + + for (int i = startIdx; i < endIdx; i++) { + int localIdx = i - startIdx; + int col = localIdx / CARDS_PER_COL; + int row = localIdx % CARDS_PER_COL; + int cardX = contentLeft + col * (colWidth + colGap); + int cardY = contentTop + row * (CARD_HEIGHT + CARD_PADDING); + GroupCardEntry card = cardEntries.get(i); + drawCard(card, cardX, cardY, colWidth); + } + + // Page counter + if (totalPages > 1) { + int navY = contentTop + CARDS_PER_COL * (CARD_HEIGHT + CARD_PADDING) + 4; + String pageText = (currentPage + 1) + "/" + totalPages; + this.drawCenteredString(this.fontRenderer, pageText, this.width / 2, navY + 6, 0xAAAAAA); + } + + // Empty state + if (cardEntries.isEmpty()) { + this.drawCenteredString(this.fontRenderer, "No collapsible groups defined", this.width / 2, contentTop + 20, 0x888888); + } + + super.drawScreen(mouseX, mouseY, partialTicks); + } + + private void drawCard(GroupCardEntry card, int x, int y, int width) { + // Card background + int bgColor = card.isCustom ? 0x40336699 : 0x40444444; + drawRect(x, y, x + width, y + CARD_HEIGHT, bgColor); + + // Border + int borderColor = card.enabled ? 0xFF558855 : 0xFF885555; + drawHorizontalLine(x, x + width - 1, y, borderColor); + drawHorizontalLine(x, x + width - 1, y + CARD_HEIGHT - 1, borderColor); + drawVerticalLine(x, y, y + CARD_HEIGHT - 1, borderColor); + drawVerticalLine(x + width - 1, y, y + CARD_HEIGHT - 1, borderColor); + + // Group name + String namePrefix = card.isCustom + ? "\u00A7e[" + Translator.translateToLocal("jei.gui.collapsible.customGroup") + "] \u00A7r" + : "\u00A77[" + Translator.translateToLocal("jei.gui.collapsible.defaultGroup") + "] \u00A7r"; + this.fontRenderer.drawStringWithShadow(namePrefix + card.displayName, x + 4, y + 4, 0xFFFFFF); + + // Item count + String countText = String.format(Translator.translateToLocal("jei.gui.collapsible.itemCount"), card.itemCount); + this.fontRenderer.drawStringWithShadow(countText, x + 4, y + 16, 0xAAAAAA); + + // Preview items + int previewY = y + 28; + int previewX = x + 4; + + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.enableDepth(); + int count = Math.min(card.previewItems.size(), PREVIEW_COLS); + for (int i = 0; i < count; i++) { + ItemStack stack = card.previewItems.get(i); + int itemX = previewX + i * (PREVIEW_SIZE + 2); + this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, itemX, previewY); + } + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + + if (card.itemCount > PREVIEW_COLS) { + String moreText = "+" + (card.itemCount - PREVIEW_COLS); + int moreX = previewX + PREVIEW_COLS * (PREVIEW_SIZE + 2) + 2; + this.fontRenderer.drawStringWithShadow(moreText, moreX, previewY + 4, 0x888888); + } + + // ID text (small, dimmed) + String idText = "\u00A78" + card.id; + this.fontRenderer.drawStringWithShadow(idText, x + 4, y + CARD_HEIGHT - 12, 0x666666); + } + + @Override + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + int scrollDelta = Mouse.getEventDWheel(); + if (scrollDelta != 0) { + if (scrollDelta < 0 && currentPage < totalPages - 1) { + currentPage++; + rebuildPageButtons(); + } else if (scrollDelta > 0 && currentPage > 0) { + currentPage--; + rebuildPageButtons(); + } + } + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + if (keyCode == 1) { // ESC + this.mc.displayGuiScreen(parentScreen); + return; + } + super.keyTyped(typedChar, keyCode); + } + + /** + * Get up to 16 preview ItemStacks for a collapsible entry by scanning the ingredient list. + */ + private List getPreviewItems(CollapsibleEntry entry) { + List items = new ArrayList<>(); + if (!Internal.hasIngredientFilter()) { + return items; + } + IngredientFilter filter = Internal.getIngredientFilter(); + List ingredientList = filter.getIngredientList(); + for (IIngredientListElement element : ingredientList) { + if (entry.matches(element)) { + Object ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + items.add((ItemStack) ingredient); + if (items.size() >= 16) { + break; + } + } + } + } + return items; + } + + /** + * Count matched items for display. + */ + private int getMatchedItemCount(CollapsibleEntry entry) { + if (!Internal.hasIngredientFilter()) { + return 0; + } + IngredientFilter filter = Internal.getIngredientFilter(); + List ingredientList = filter.getIngredientList(); + int count = 0; + for (IIngredientListElement element : ingredientList) { + if (entry.matches(element)) { + count++; + } + } + return count; + } + + /** + * Called when returning from the editor screen to refresh the card list. + */ + public void onEditorClosed() { + rebuildCards(); + rebuildPageButtons(); + } + + private static class GroupCardEntry { + final String id; + final String displayName; + final boolean isCustom; + boolean enabled; + final List previewItems; + final int itemCount; + + GroupCardEntry(String id, String displayName, boolean isCustom, boolean enabled, List previewItems, int itemCount) { + this.id = id; + this.displayName = displayName; + this.isCustom = isCustom; + this.enabled = enabled; + this.previewItems = previewItems; + this.itemCount = itemCount; + } + } +} diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java new file mode 100644 index 000000000..cf17fac12 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -0,0 +1,556 @@ +package mezz.jei.gui.overlay.collapsible; + +import mezz.jei.Internal; +import mezz.jei.api.ingredients.IIngredientRenderer; +import mezz.jei.config.Config; +import mezz.jei.config.CustomGroupsConfig; +import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.ingredients.IngredientFilter; +import mezz.jei.startup.StackHelper; +import mezz.jei.util.Translator; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.client.config.GuiUtils; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.*; + +/** + * Editor screen for a custom collapsible group. + * Left panel: searchable item grid for selection. + * Right panel: preview of selected items. + */ +public class GuiCustomGroupEditor extends GuiScreen { + + private static final int ITEM_SIZE = 18; + private static final int GRID_PADDING = 2; + + private static final int BTN_SAVE = 0; + private static final int BTN_CANCEL = 1; + private static final int BTN_PREV_PAGE = 2; + private static final int BTN_NEXT_PAGE = 3; + private static final int BTN_PREV_SEL_PAGE = 4; + private static final int BTN_NEXT_SEL_PAGE = 5; + + private final GuiCollapsibleGroups parentScreen; + private final CustomGroupsConfig.CustomGroup group; + private final Set selectedUids = new LinkedHashSet<>(); + + @Nullable + private GuiTextField nameField; + @Nullable + private GuiTextField searchField; + + // Left grid (all items) + private List filteredItems = Collections.emptyList(); + private int leftCols; + private int leftRows; + private int leftGridX; + private int leftGridY; + private int leftPage = 0; + private int leftTotalPages = 1; + private int leftItemsPerPage; + + // Right grid (selected items) + private int rightCols; + private int rightRows; + private int rightGridX; + private int rightGridY; + private int rightPage = 0; + private int rightTotalPages = 1; + private int rightItemsPerPage; + + // Cached selected stacks for the right panel + private List selectedStacks = new ArrayList<>(); + + public GuiCustomGroupEditor(GuiCollapsibleGroups parentScreen, CustomGroupsConfig.CustomGroup group) { + this.parentScreen = parentScreen; + this.group = group; + this.selectedUids.addAll(group.itemUids); + } + + @Override + public void initGui() { + super.initGui(); + Keyboard.enableRepeatEvents(true); + this.buttonList.clear(); + + int topBarHeight = 48; + int panelDivider = (int) (this.width * 0.65); + + // Name field + nameField = new GuiTextField(10, this.fontRenderer, 60, 6, panelDivider - 70, 16); + nameField.setMaxStringLength(40); + nameField.setText(group.displayName != null ? group.displayName : ""); + + // Search field + searchField = new GuiTextField(11, this.fontRenderer, 4, topBarHeight - 18, panelDivider - 12, 14); + searchField.setMaxStringLength(128); + searchField.setText(""); + + // Save & Cancel buttons + this.buttonList.add(new GuiButton(BTN_SAVE, panelDivider + 4, 4, 50, 18, + Translator.translateToLocal("jei.gui.collapsible.editor.save"))); + this.buttonList.add(new GuiButton(BTN_CANCEL, panelDivider + 58, 4, 50, 18, + Translator.translateToLocal("jei.gui.collapsible.back"))); + + // Calculate left grid layout + int leftWidth = panelDivider - 8; + int leftHeight = this.height - topBarHeight - 26; // room for page nav + leftCols = Math.max(1, leftWidth / ITEM_SIZE); + leftRows = Math.max(1, leftHeight / ITEM_SIZE); + leftGridX = 4; + leftGridY = topBarHeight; + leftItemsPerPage = leftCols * leftRows; + + // Calculate right grid layout + int rightWidth = this.width - panelDivider - 8; + int rightHeight = this.height - topBarHeight - 26; + rightCols = Math.max(1, rightWidth / ITEM_SIZE); + rightRows = Math.max(1, rightHeight / ITEM_SIZE); + rightGridX = panelDivider + 4; + rightGridY = topBarHeight; + rightItemsPerPage = rightCols * rightRows; + + // Page nav buttons for left grid + int leftNavY = this.height - 22; + this.buttonList.add(new GuiButton(BTN_PREV_PAGE, 4, leftNavY, 30, 18, "<")); + this.buttonList.add(new GuiButton(BTN_NEXT_PAGE, panelDivider - 34, leftNavY, 30, 18, ">")); + + // Page nav buttons for right grid + this.buttonList.add(new GuiButton(BTN_PREV_SEL_PAGE, panelDivider + 4, leftNavY, 30, 18, "<")); + this.buttonList.add(new GuiButton(BTN_NEXT_SEL_PAGE, this.width - 34, leftNavY, 30, 18, ">")); + + updateFilteredItems(); + updateSelectedStacks(); + } + + @Override + public void onGuiClosed() { + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } + + private void updateFilteredItems() { + if (!Internal.hasIngredientFilter()) { + filteredItems = Collections.emptyList(); + return; + } + IngredientFilter filter = Internal.getIngredientFilter(); + String search = (searchField != null) ? searchField.getText() : ""; + filteredItems = filter.getIngredientList(search); + leftTotalPages = Math.max(1, (filteredItems.size() + leftItemsPerPage - 1) / leftItemsPerPage); + if (leftPage >= leftTotalPages) { + leftPage = leftTotalPages - 1; + } + } + + private void updateSelectedStacks() { + selectedStacks.clear(); + if (!Internal.hasIngredientFilter()) { + return; + } + StackHelper stackHelper = Internal.getStackHelper(); + IngredientFilter filter = Internal.getIngredientFilter(); + // Get the full ingredient list and find items matching selectedUids + List all = filter.getIngredientList(""); + for (IIngredientListElement element : all) { + Object ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + ItemStack stack = (ItemStack) ingredient; + if (!stack.isEmpty()) { + try { + String uid = stackHelper.getUniqueIdentifierForStack(stack); + if (selectedUids.contains(uid)) { + selectedStacks.add(stack); + } + } catch (Exception ignored) { + } + } + } + } + rightTotalPages = Math.max(1, (selectedStacks.size() + rightItemsPerPage - 1) / rightItemsPerPage); + if (rightPage >= rightTotalPages) { + rightPage = rightTotalPages - 1; + } + } + + @Override + protected void actionPerformed(GuiButton button) throws IOException { + switch (button.id) { + case BTN_SAVE: + saveAndClose(); + break; + case BTN_CANCEL: + this.mc.displayGuiScreen(parentScreen); + break; + case BTN_PREV_PAGE: + leftPage = Math.max(0, leftPage - 1); + break; + case BTN_NEXT_PAGE: + leftPage = Math.min(leftTotalPages - 1, leftPage + 1); + break; + case BTN_PREV_SEL_PAGE: + rightPage = Math.max(0, rightPage - 1); + break; + case BTN_NEXT_SEL_PAGE: + rightPage = Math.min(rightTotalPages - 1, rightPage + 1); + break; + } + } + + private void saveAndClose() { + if (nameField != null) { + group.displayName = nameField.getText(); + } + group.itemUids = new ArrayList<>(selectedUids); + + CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); + if (customGroupsConfig != null) { + customGroupsConfig.updateGroup(group); + Internal.getCollapsibleEntryRegistry().recollectCustomEntries(); + if (Internal.hasIngredientFilter()) { + IngredientFilter filter = Internal.getIngredientFilter(); + // Invalidate the filter cache so the next call to getIngredientList + // rebuilds collapsedListCached with the new custom entries. + filter.invalidateCache(); + filter.getIngredientList(Config.getFilterText()); + filter.notifyListenersOfChange(); + } + } + parentScreen.onEditorClosed(); + this.mc.displayGuiScreen(parentScreen); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + this.drawDefaultBackground(); + + int panelDivider = (int) (this.width * 0.65); + + // Name label + this.fontRenderer.drawStringWithShadow( + Translator.translateToLocal("jei.gui.collapsible.editor.name") + ":", + 4, 10, 0xFFFFFF); + if (nameField != null) { + nameField.drawTextBox(); + } + + // Search field + if (searchField != null) { + searchField.drawTextBox(); + } + + // Divider line + drawVerticalLine(panelDivider, 0, this.height, 0xFF555555); + + // "Selected" header on right panel + String selHeader = Translator.translateToLocal("jei.gui.collapsible.editor.title"); + this.fontRenderer.drawStringWithShadow(selHeader, panelDivider + 4, 26, 0xCCCCCC); + String selCount = String.format(Translator.translateToLocal("jei.gui.collapsible.editor.selected"), selectedUids.size()); + this.fontRenderer.drawStringWithShadow(selCount, panelDivider + 4, 36, 0x888888); + + // Draw left grid (all items) + drawLeftGrid(mouseX, mouseY); + + // Draw right grid (selected items) + drawRightGrid(mouseX, mouseY); + + // Left page counter + if (leftTotalPages > 1) { + int navY = this.height - 18; + String pageText = (leftPage + 1) + "/" + leftTotalPages; + int centerX = (4 + panelDivider) / 2; + this.drawCenteredString(this.fontRenderer, pageText, centerX, navY, 0xAAAAAA); + } + + // Right page counter + if (rightTotalPages > 1) { + int navY = this.height - 18; + String pageText = (rightPage + 1) + "/" + rightTotalPages; + int centerX = (panelDivider + 4 + this.width) / 2; + this.drawCenteredString(this.fontRenderer, pageText, centerX, navY, 0xAAAAAA); + } + + super.drawScreen(mouseX, mouseY, partialTicks); + + // Draw tooltips last (after buttons) + drawLeftGridTooltips(mouseX, mouseY); + drawRightGridTooltips(mouseX, mouseY); + } + + private void drawLeftGrid(int mouseX, int mouseY) { + if (filteredItems.isEmpty()) { + return; + } + StackHelper stackHelper = Internal.getStackHelper(); + int startIdx = leftPage * leftItemsPerPage; + + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.enableDepth(); + + for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { + IIngredientListElement element = filteredItems.get(startIdx + i); + int col = i % leftCols; + int row = i / leftCols; + int x = leftGridX + col * ITEM_SIZE; + int y = leftGridY + row * ITEM_SIZE; + + Object ingredient = element.getIngredient(); + renderIngredient(element, x + 1, y + 1); + + // Green overlay if selected + if (ingredient instanceof ItemStack) { + try { + String uid = stackHelper.getUniqueIdentifierForStack((ItemStack) ingredient); + if (selectedUids.contains(uid)) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + GlStateManager.colorMask(true, true, true, false); + GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x4000FF00, 0x4000FF00); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableDepth(); + RenderHelper.enableGUIStandardItemLighting(); + } + } catch (Exception ignored) { + } + } + + // Highlight on hover + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + GlStateManager.colorMask(true, true, true, false); + GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x80FFFFFF, 0x80FFFFFF); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableDepth(); + RenderHelper.enableGUIStandardItemLighting(); + } + } + + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + } + + private void drawRightGrid(int mouseX, int mouseY) { + if (selectedStacks.isEmpty()) { + return; + } + int startIdx = rightPage * rightItemsPerPage; + + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.enableDepth(); + + for (int i = 0; i < rightItemsPerPage && (startIdx + i) < selectedStacks.size(); i++) { + ItemStack stack = selectedStacks.get(startIdx + i); + int col = i % rightCols; + int row = i / rightCols; + int x = rightGridX + col * ITEM_SIZE; + int y = rightGridY + row * ITEM_SIZE; + + this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, x + 1, y + 1); + + // Highlight on hover + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + GlStateManager.colorMask(true, true, true, false); + GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x80FF8888, 0x80FF8888); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableDepth(); + RenderHelper.enableGUIStandardItemLighting(); + } + } + + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + } + + @SuppressWarnings("unchecked") + private void renderIngredient(IIngredientListElement element, int x, int y) { + try { + IIngredientRenderer renderer = element.getIngredientRenderer(); + T ingredient = element.getIngredient(); + renderer.render(this.mc, x, y, ingredient); + } catch (Exception ignored) { + } + } + + private void drawLeftGridTooltips(int mouseX, int mouseY) { + if (filteredItems.isEmpty()) { + return; + } + int startIdx = leftPage * leftItemsPerPage; + for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { + int col = i % leftCols; + int row = i / leftCols; + int x = leftGridX + col * ITEM_SIZE; + int y = leftGridY + row * ITEM_SIZE; + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + IIngredientListElement element = filteredItems.get(startIdx + i); + Object ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + renderToolTip((ItemStack) ingredient, mouseX, mouseY); + } + return; + } + } + } + + private void drawRightGridTooltips(int mouseX, int mouseY) { + if (selectedStacks.isEmpty()) { + return; + } + int startIdx = rightPage * rightItemsPerPage; + for (int i = 0; i < rightItemsPerPage && (startIdx + i) < selectedStacks.size(); i++) { + int col = i % rightCols; + int row = i / rightCols; + int x = rightGridX + col * ITEM_SIZE; + int y = rightGridY + row * ITEM_SIZE; + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + renderToolTip(selectedStacks.get(startIdx + i), mouseX, mouseY); + return; + } + } + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + if (nameField != null) { + nameField.mouseClicked(mouseX, mouseY, mouseButton); + } + if (searchField != null) { + boolean wasSearchFocused = searchField.isFocused(); + searchField.mouseClicked(mouseX, mouseY, mouseButton); + // Right-click clears search + if (searchField.isFocused() && mouseButton == 1) { + searchField.setText(""); + updateFilteredItems(); + } + } + + // Left grid click: toggle item selection + if (mouseButton == 0 && !filteredItems.isEmpty()) { + int startIdx = leftPage * leftItemsPerPage; + for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { + int col = i % leftCols; + int row = i / leftCols; + int x = leftGridX + col * ITEM_SIZE; + int y = leftGridY + row * ITEM_SIZE; + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + IIngredientListElement element = filteredItems.get(startIdx + i); + Object ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + toggleSelection((ItemStack) ingredient); + } + return; + } + } + } + + // Right grid click: remove from selection + if (mouseButton == 0 && !selectedStacks.isEmpty()) { + int startIdx = rightPage * rightItemsPerPage; + for (int i = 0; i < rightItemsPerPage && (startIdx + i) < selectedStacks.size(); i++) { + int col = i % rightCols; + int row = i / rightCols; + int x = rightGridX + col * ITEM_SIZE; + int y = rightGridY + row * ITEM_SIZE; + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + ItemStack stack = selectedStacks.get(startIdx + i); + removeSelection(stack); + return; + } + } + } + + super.mouseClicked(mouseX, mouseY, mouseButton); + } + + private void toggleSelection(ItemStack stack) { + try { + StackHelper stackHelper = Internal.getStackHelper(); + String uid = stackHelper.getUniqueIdentifierForStack(stack); + if (selectedUids.contains(uid)) { + selectedUids.remove(uid); + } else { + selectedUids.add(uid); + } + updateSelectedStacks(); + } catch (Exception ignored) { + } + } + + private void removeSelection(ItemStack stack) { + try { + StackHelper stackHelper = Internal.getStackHelper(); + String uid = stackHelper.getUniqueIdentifierForStack(stack); + selectedUids.remove(uid); + updateSelectedStacks(); + } catch (Exception ignored) { + } + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + if (nameField != null && nameField.isFocused()) { + nameField.textboxKeyTyped(typedChar, keyCode); + return; + } + if (searchField != null && searchField.isFocused()) { + searchField.textboxKeyTyped(typedChar, keyCode); + updateFilteredItems(); + return; + } + if (keyCode == Keyboard.KEY_ESCAPE) { + this.mc.displayGuiScreen(parentScreen); + return; + } + super.keyTyped(typedChar, keyCode); + } + + @Override + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + int scrollDelta = Mouse.getEventDWheel(); + if (scrollDelta != 0) { + int mouseX = Mouse.getEventX() * this.width / this.mc.displayWidth; + int panelDivider = (int) (this.width * 0.65); + + if (mouseX < panelDivider) { + // Scroll left grid + if (scrollDelta < 0 && leftPage < leftTotalPages - 1) { + leftPage++; + } else if (scrollDelta > 0 && leftPage > 0) { + leftPage--; + } + } else { + // Scroll right grid + if (scrollDelta < 0 && rightPage < rightTotalPages - 1) { + rightPage++; + } else if (scrollDelta > 0 && rightPage > 0) { + rightPage--; + } + } + } + } + + @Override + public void updateScreen() { + super.updateScreen(); + if (nameField != null) { + nameField.updateCursorCounter(); + } + if (searchField != null) { + searchField.updateCursorCounter(); + } + } +} diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java index adedc0bad..b1bf5e55a 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java @@ -1,5 +1,11 @@ package mezz.jei.ingredients; +import mezz.jei.Internal; +import mezz.jei.config.Config; +import mezz.jei.config.CustomGroupsConfig; +import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.startup.StackHelper; +import mezz.jei.util.Log; import net.minecraft.item.ItemStack; import javax.annotation.Nullable; @@ -15,6 +21,7 @@ public class CollapsibleEntryRegistry { private static CollapsibleEntryRegistry instance; private final LinkedHashMap entries = new LinkedHashMap<>(); + private final List customEntries = new ArrayList<>(); private final Set disabledGroups = new HashSet<>(); public static CollapsibleEntryRegistry getInstance() { @@ -63,4 +70,53 @@ public void setDisabledGroups(Collection disabled) { public boolean isGroupEnabled(String id) { return !disabledGroups.contains(id); } + + public List getCustomEntries() { + return customEntries; + } + + /** + * Load custom collapsible groups from the JSON config. + * Creates CollapsibleEntry objects that match items by their unique identifier. + */ + public void loadCustomGroups() { + customEntries.clear(); + CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); + if (customGroupsConfig == null) { + return; + } + for (CustomGroupsConfig.CustomGroup group : customGroupsConfig.getCustomGroups()) { + if (group.id == null || group.id.isEmpty() || group.itemUids == null) { + continue; + } + Set uidSet = new HashSet<>(group.itemUids); + Predicate matcher = stack -> { + try { + StackHelper stackHelper = Internal.getStackHelper(); + String uid = stackHelper.getUniqueIdentifierForStack(stack); + return uidSet.contains(uid); + } catch (Exception e) { + return false; + } + }; + String displayName = group.displayName != null ? group.displayName : group.id; + customEntries.add(new CollapsibleEntry(group.id, displayName, matcher)); + } + Log.get().debug("Loaded {} custom collapsible groups", customEntries.size()); + } + + /** + * Reload custom entries from config. Called after saving changes. + */ + public void recollectCustomEntries() { + loadCustomGroups(); + } + + /** + * Sync disabled group state from Config values. + */ + public void syncDisabledGroups() { + this.disabledGroups.clear(); + this.disabledGroups.addAll(Config.getDisabledGroups()); + } } diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index 392f4e13b..adc775ed7 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -264,7 +264,8 @@ private List collapse(List ingredientList) { } CollapsibleEntryRegistry registry = CollapsibleEntryRegistry.getInstance(); Collection entries = registry.getEntries(); - if (entries.isEmpty()) { + List customEntries = registry.getCustomEntries(); + if (entries.isEmpty() && customEntries.isEmpty()) { return new ArrayList<>(ingredientList); } @@ -275,6 +276,11 @@ private List collapse(List ingredientList) { activeEntries.add(entry); } } + for (CollapsibleEntry entry : customEntries) { + if (registry.isGroupEnabled(entry.getId())) { + activeEntries.add(entry); + } + } if (activeEntries.isEmpty()) { return new ArrayList<>(ingredientList); } diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index d6253d4d2..e3d448af3 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -103,6 +103,12 @@ public void load(List plugins, Textures textures, boolean recipesOnl registerDefaultCollapsibleGroups(); + { + CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + registry.loadCustomGroups(); + registry.syncDisabledGroups(); + } + BookmarkList bookmarkList = new BookmarkList(ingredientRegistry); Internal.setBookmarkList(bookmarkList); diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index 5d86e0dc2..3f8084fbf 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -211,4 +211,17 @@ hei.tooltip.organizer.1=§6CTRL + LEFT CLICK§7: Move Bookmarks Between Groups hei.tooltip.organizer.2=§6UP§r or §6DOWN§7: Move Group Up or Down hei.tooltip.organizer.3=§6CTRL + SCROLL§7: Change Final Output hei.tooltip.organizer.4=§6%s§7: Autocraft + +jei.gui.collapsible.title=Manage Groups +jei.gui.collapsible.back=Back +jei.gui.collapsible.newGroup=New Group +jei.gui.collapsible.enabled=Enabled +jei.gui.collapsible.disabled=Disabled +jei.gui.collapsible.customGroup=Custom +jei.gui.collapsible.defaultGroup=Default +jei.gui.collapsible.itemCount=%d items +jei.gui.collapsible.editor.title=Selected Items +jei.gui.collapsible.editor.name=Name +jei.gui.collapsible.editor.save=Save +jei.gui.collapsible.editor.selected=%d selected hei.tooltip.missing_ingredients=§cMissing Ingredients: \ No newline at end of file From fa64f471ea73555d9a3528c7815da97be23390d6 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 02:14:01 -0400 Subject: [PATCH 04/48] Configuration Cleanup Moved the configuration buttons below the blacklist. One entry for bool enable for groups and one for the groups submenu. --- .../java/mezz/jei/config/JEIModConfigGui.java | 21 ++++++++++++++----- .../collapsible/GuiCollapsibleGroups.java | 8 +++---- src/main/resources/assets/jei/lang/en_us.lang | 1 + 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/mezz/jei/config/JEIModConfigGui.java b/src/main/java/mezz/jei/config/JEIModConfigGui.java index 69cef12ce..2dd257e4b 100644 --- a/src/main/java/mezz/jei/config/JEIModConfigGui.java +++ b/src/main/java/mezz/jei/config/JEIModConfigGui.java @@ -19,7 +19,9 @@ import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.client.resources.I18n; import net.minecraft.network.NetworkManager; +import net.minecraftforge.fml.client.config.GuiButtonExt; import mezz.jei.JustEnoughItems; import mezz.jei.gui.overlay.collapsible.GuiCollapsibleGroups; @@ -143,15 +145,24 @@ protected void actionPerformed(GuiButton button) { * A CategoryEntry that opens GuiCollapsibleGroups instead of a standard GuiConfig subcategory. * Constructor signature must match (GuiConfig, GuiConfigEntries, IConfigElement). */ - public static class ManageGroupsEntry extends GuiConfigEntries.CategoryEntry { + public static class ManageGroupsEntry extends GuiConfigEntries.ButtonEntry { public ManageGroupsEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement configElement) { - super(owningScreen, owningEntryList, configElement); + super(owningScreen, owningEntryList, configElement, + new GuiButtonExt(0, owningEntryList.controlX, 0, owningEntryList.controlWidth, 18, + I18n.format("jei.gui.collapsible.title"))); } - @Override - protected GuiScreen buildChildScreen() { - return new GuiCollapsibleGroups(owningScreen); + @Override public void updateValueButtonText() {} + @Override public void valueButtonPressed(int slotIndex) { + this.mc.displayGuiScreen(new GuiCollapsibleGroups(this.owningScreen)); } + @Override public boolean isDefault() { return true; } + @Override public void setToDefault() {} + @Override public boolean isChanged() { return false; } + @Override public void undoChanges() {} + @Override public boolean saveConfigElement() { return false; } + @Override public Object getCurrentValue() { return ""; } + @Override public Object[] getCurrentValues() { return new Object[]{ "" }; } } /** diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 66e30eee2..97cb43619 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -63,7 +63,7 @@ public void initGui() { // New Group button this.buttonList.add(new GuiButton(BTN_NEW, this.width - 64, 4, 60, 20, - "+ " + Translator.translateToLocal("jei.gui.collapsible.newGroup"))); + Translator.translateToLocal("jei.gui.collapsible.newGroup"))); rebuildCards(); rebuildPageButtons(); @@ -124,14 +124,14 @@ private void rebuildPageButtons() { String toggleLabel = card.enabled ? Translator.translateToLocal("jei.gui.collapsible.enabled") : Translator.translateToLocal("jei.gui.collapsible.disabled"); - this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 14, toggleLabel)); + this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 16, toggleLabel)); if (card.isCustom) { // Configure button - this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 16, 24, 14, + this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 18, 24, 16, "\u270E")); // pencil unicode // Delete button - this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 16, 26, 14, + this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 18, 26, 16, "\u2716")); // cross unicode } } diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index 3f8084fbf..65b5d612b 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -215,6 +215,7 @@ hei.tooltip.organizer.4=§6%s§7: Autocraft jei.gui.collapsible.title=Manage Groups jei.gui.collapsible.back=Back jei.gui.collapsible.newGroup=New Group +jei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user defined groups. jei.gui.collapsible.enabled=Enabled jei.gui.collapsible.disabled=Disabled jei.gui.collapsible.customGroup=Custom From ce91f6148b8f9176c40742cc200025ecfa435303 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 02:35:56 -0400 Subject: [PATCH 05/48] Fix hide ingredients mode. While hide mode is enabled, we just expand all the collapsed entries. --- .../java/mezz/jei/ingredients/IngredientFilter.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index adc775ed7..cc4830c08 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -142,6 +142,19 @@ public void modesChanged() { public void onEditModeToggleEvent(EditModeToggleEvent event) { this.filterCached = null; updateHidden(); + + // In Hide Ingredients Mode the user cannot Alt+Click to expand/collapse groups, + // so expand all groups when entering edit mode and collapse them on exit. + boolean editMode = event.isEditModeEnabled(); + CollapsibleEntryRegistry registry = mezz.jei.Internal.getCollapsibleEntryRegistry(); + for (CollapsibleEntry entry : registry.getEntries()) { + entry.setExpanded(editMode); + } + for (CollapsibleEntry entry : registry.getCustomEntries()) { + entry.setExpanded(editMode); + } + this.collapsedListCached = Collections.emptyList(); + notifyCollapsedStateChanged(); } public void updateHidden() { From ce72aef4317a0e42354cb8a50c4d916be7c871c5 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 02:42:40 -0400 Subject: [PATCH 06/48] Fix Enchanted Book Groups --- .../jei/ingredients/CollapsibleEntry.java | 32 +++++++++++++------ .../ingredients/CollapsibleEntryRegistry.java | 29 +++++++++++++---- .../java/mezz/jei/startup/JeiStarter.java | 8 +++-- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java index 0cf068c3c..1d146aef4 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java @@ -9,20 +9,36 @@ * Defines a collapsible group of ingredients in the ingredient list. * When collapsed, all matching ingredients are shown as a single entry. * When expanded, all matching ingredients appear individually. + * + * The matcher operates on the raw ingredient object so that non-ItemStack + * ingredient types (e.g. EnchantmentData for enchanted books) can be matched. */ public class CollapsibleEntry { private final String id; private final String displayName; - private final Predicate matcher; + /** Matches against the raw ingredient object (any type). */ + private final Predicate matcher; private boolean expanded; - public CollapsibleEntry(String id, String displayName, Predicate matcher) { + /** + * Primary constructor — matcher receives the raw ingredient object. + */ + public CollapsibleEntry(String id, String displayName, Predicate matcher) { this.id = id; this.displayName = displayName; this.matcher = matcher; this.expanded = false; } + /** + * Convenience constructor for groups that only care about ItemStack ingredients. + * Non-ItemStack ingredients automatically return false. + */ + public static CollapsibleEntry ofItemStack(String id, String displayName, Predicate stackMatcher) { + return new CollapsibleEntry(id, displayName, + ingredient -> ingredient instanceof ItemStack && stackMatcher.test((ItemStack) ingredient)); + } + public String getId() { return id; } @@ -45,16 +61,14 @@ public void toggleExpanded() { /** * Tests whether the given element matches this collapsible group. - * Only ItemStack ingredients are tested; non-ItemStack ingredients never match. + * The raw ingredient (any type) is passed to the matcher. + * Empty ItemStacks are always rejected. */ public boolean matches(IIngredientListElement element) { Object ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack) { - ItemStack itemStack = (ItemStack) ingredient; - if (!itemStack.isEmpty()) { - return matcher.test(itemStack); - } + if (ingredient instanceof ItemStack && ((ItemStack) ingredient).isEmpty()) { + return false; } - return false; + return matcher.test(ingredient); } } diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java index b1bf5e55a..374d0ad00 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java @@ -36,12 +36,27 @@ public static void setInstance(@Nullable CollapsibleEntryRegistry registry) { } /** - * Register a collapsible group with a predicate matcher. - * @param id unique identifier for the group + * Register a collapsible group whose membership is determined by an ItemStack predicate. + * Non-ItemStack ingredients are automatically excluded. + * + * @param id unique identifier for the group * @param displayName localized display name - * @param matcher predicate that returns true for ItemStacks belonging to this group + * @param matcher predicate that returns true for ItemStacks belonging to this group */ public void group(String id, String displayName, Predicate matcher) { + entries.put(id, CollapsibleEntry.ofItemStack(id, displayName, matcher)); + } + + /** + * Register a collapsible group whose membership is determined by a predicate on the + * raw ingredient object. Use this when the ingredients are not ItemStacks + * (e.g. EnchantmentData for enchanted books). + * + * @param id unique identifier for the group + * @param displayName localized display name + * @param matcher predicate on the raw ingredient object + */ + public void groupForType(String id, String displayName, Predicate matcher) { entries.put(id, new CollapsibleEntry(id, displayName, matcher)); } @@ -90,7 +105,9 @@ public void loadCustomGroups() { continue; } Set uidSet = new HashSet<>(group.itemUids); - Predicate matcher = stack -> { + String displayName = group.displayName != null ? group.displayName : group.id; + // Custom groups only ever contain ItemStacks identified by UID. + customEntries.add(CollapsibleEntry.ofItemStack(group.id, displayName, stack -> { try { StackHelper stackHelper = Internal.getStackHelper(); String uid = stackHelper.getUniqueIdentifierForStack(stack); @@ -98,9 +115,7 @@ public void loadCustomGroups() { } catch (Exception e) { return false; } - }; - String displayName = group.displayName != null ? group.displayName : group.id; - customEntries.add(new CollapsibleEntry(group.id, displayName, matcher)); + })); } Log.get().debug("Loaded {} custom collapsible groups", customEntries.size()); } diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index e3d448af3..5004fb6ab 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -35,7 +35,6 @@ import mezz.jei.util.Log; import mezz.jei.util.LoggedTimer; import net.minecraft.init.Items; -import net.minecraft.item.ItemEnchantedBook; import net.minecraft.item.ItemMonsterPlacer; import net.minecraftforge.fml.common.ProgressManager; @@ -350,8 +349,11 @@ private static void sendRuntime(List plugins, IJeiRuntime jeiRuntime private static void registerDefaultCollapsibleGroups() { CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); - registry.group("enchanted_books", "Enchanted Books", - stack -> stack.getItem() instanceof ItemEnchantedBook); + // Enchanted books in JEI are stored as EnchantmentData (VanillaTypes.ENCHANT), + // not as ItemStacks — IngredientRegistry strips them from the ItemStack list. + // Match all EnchantmentData directly; every EnchantmentData IS an enchanted book. + registry.groupForType("enchanted_books", "Enchanted Books", + ingredient -> ingredient instanceof net.minecraft.enchantment.EnchantmentData); registry.group("potions", "Potions", stack -> stack.getItem() == Items.POTIONITEM); registry.group("splash_potions", "Splash Potions", From 1f66391e04d1907f40cecb7b4d64f7d2b2852ddd Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 03:04:24 -0400 Subject: [PATCH 07/48] Searchable "Enchanted Book" Amend Enchanted Book to item name making it searchable in non-prefix search. --- .../vanilla/ingredients/enchant/EnchantDataHelper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java b/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java index 1300d0699..389f52b2c 100644 --- a/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java +++ b/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java @@ -4,6 +4,7 @@ import java.awt.Color; import net.minecraft.enchantment.EnchantmentData; +import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; @@ -41,7 +42,10 @@ public IFocus translateFocus(IFocus focus, IFocusFactory foc @Override public String getDisplayName(EnchantmentData ingredient) { - return ingredient.enchantment.getTranslatedName(ingredient.enchantmentLevel); + // Prefix with the item's localized name so that searching "enchanted" (or "book") + // finds all enchanted-book entries via the NO_PREFIX search tree. + String itemName = new ItemStack(Items.ENCHANTED_BOOK).getDisplayName(); + return itemName + " - " + ingredient.enchantment.getTranslatedName(ingredient.enchantmentLevel); } @Override From c95b249e97e2647dab9a2b8704ea34ef397c2f32 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 04:06:12 -0400 Subject: [PATCH 08/48] Update GuiCollapsibleGroups.java Fixed search result being applied to groups management page Fixed enchanted book previews Increased rows to 3 Removed ID string from display Fixed GUI scaling on group management page. --- .../collapsible/GuiCollapsibleGroups.java | 151 +++++++++++------- 1 file changed, 95 insertions(+), 56 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 97cb43619..f15829faa 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -1,6 +1,7 @@ package mezz.jei.gui.overlay.collapsible; import mezz.jei.Internal; +import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.config.Config; import mezz.jei.config.CustomGroupsConfig; import mezz.jei.gui.ingredients.IIngredientListElement; @@ -28,12 +29,20 @@ */ public class GuiCollapsibleGroups extends GuiScreen { - private static final int CARD_HEIGHT = 60; + private static final int CARD_HEIGHT = 84; private static final int CARD_PADDING = 4; private static final int PREVIEW_SIZE = 16; private static final int PREVIEW_COLS = 8; - private static final int CARDS_PER_COL = 3; - private static final int CARDS_PER_PAGE = 6; // 2 columns × 3 rows + private static final int PREVIEW_ROWS = 3; + + // Dynamic layout — recomputed on each initGui() call so the screen adapts to GUI scale + private int cardsPerCol; + private int cardsPerPage; + private int layoutContentTop; + private int layoutContentWidth; + private int layoutContentLeft; + private int layoutColGap; + private int layoutColWidth; private static final int BTN_BACK = 0; private static final int BTN_NEW = 1; @@ -47,22 +56,37 @@ public class GuiCollapsibleGroups extends GuiScreen { private final List cardEntries = new ArrayList<>(); private int currentPage = 0; private int totalPages = 1; + @Nullable private ItemStack tooltipStack = null; public GuiCollapsibleGroups(GuiScreen parentScreen) { this.parentScreen = parentScreen; } + private void computeLayout() { + layoutContentTop = 30; + layoutColGap = 4; + // Use up to 90% of screen width, capped at 700 so wide monitors still look reasonable + layoutContentWidth = Math.min(this.width - 20, Math.max(300, (int) (this.width * 0.9))); + layoutContentLeft = (this.width - layoutContentWidth) / 2; + layoutColWidth = (layoutContentWidth - layoutColGap) / 2; + // Reserve: header (30) + header buttons (24) + nav row (28) + bottom margin (6) + int availableForCards = this.height - layoutContentTop - 28 - 6; + cardsPerCol = Math.max(1, availableForCards / (CARD_HEIGHT + CARD_PADDING)); + cardsPerPage = 2 * cardsPerCol; + } + @Override public void initGui() { super.initGui(); this.buttonList.clear(); + computeLayout(); // Back button this.buttonList.add(new GuiButton(BTN_BACK, 4, 4, 60, 20, Translator.translateToLocal("jei.gui.collapsible.back"))); // New Group button - this.buttonList.add(new GuiButton(BTN_NEW, this.width - 64, 4, 60, 20, + this.buttonList.add(new GuiButton(BTN_NEW, layoutContentLeft + layoutContentWidth - 62, 4, 60, 20, Translator.translateToLocal("jei.gui.collapsible.newGroup"))); rebuildCards(); @@ -90,7 +114,7 @@ private void rebuildCards() { !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); } - totalPages = Math.max(1, (cardEntries.size() + CARDS_PER_PAGE - 1) / CARDS_PER_PAGE); + totalPages = Math.max(1, (cardEntries.size() + cardsPerPage - 1) / cardsPerPage); if (currentPage >= totalPages) { currentPage = totalPages - 1; } @@ -100,24 +124,18 @@ private void rebuildPageButtons() { // Remove old card-specific and page buttons buttonList.removeIf(b -> b.id >= BTN_PREV_PAGE); - int contentTop = 30; - int contentWidth = Math.min(this.width - 10, 480); - int contentLeft = (this.width - contentWidth) / 2; - int colGap = 4; - int colWidth = (contentWidth - colGap) / 2; - - int startIdx = currentPage * CARDS_PER_PAGE; - int endIdx = Math.min(startIdx + CARDS_PER_PAGE, cardEntries.size()); + int startIdx = currentPage * cardsPerPage; + int endIdx = Math.min(startIdx + cardsPerPage, cardEntries.size()); for (int i = startIdx; i < endIdx; i++) { int localIdx = i - startIdx; - int col = localIdx / CARDS_PER_COL; // 0 = left, 1 = right - int row = localIdx % CARDS_PER_COL; - int cardX = contentLeft + col * (colWidth + colGap); - int cardY = contentTop + row * (CARD_HEIGHT + CARD_PADDING); + int col = localIdx / cardsPerCol; // 0 = left, 1 = right + int row = localIdx % cardsPerCol; + int cardX = layoutContentLeft + col * (layoutColWidth + layoutColGap); + int cardY = layoutContentTop + row * (CARD_HEIGHT + CARD_PADDING); GroupCardEntry card = cardEntries.get(i); - int btnX = cardX + colWidth - 56; + int btnX = cardX + layoutColWidth - 56; int btnY = cardY + 4; // Toggle button @@ -138,9 +156,9 @@ private void rebuildPageButtons() { // Page navigation if (totalPages > 1) { - int navY = contentTop + CARDS_PER_COL * (CARD_HEIGHT + CARD_PADDING) + 4; - this.buttonList.add(new GuiButton(BTN_PREV_PAGE, contentLeft, navY, 40, 20, "<")); - this.buttonList.add(new GuiButton(BTN_NEXT_PAGE, contentLeft + contentWidth - 40, navY, 40, 20, ">")); + int navY = layoutContentTop + cardsPerCol * (CARD_HEIGHT + CARD_PADDING) + 4; + this.buttonList.add(new GuiButton(BTN_PREV_PAGE, layoutContentLeft, navY, 40, 20, "<")); + this.buttonList.add(new GuiButton(BTN_NEXT_PAGE, layoutContentLeft + layoutContentWidth - 40, navY, 40, 20, ">")); } } @@ -255,41 +273,40 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { String title = Translator.translateToLocal("jei.gui.collapsible.title"); this.drawCenteredString(this.fontRenderer, title, this.width / 2, 10, 0xFFFFFF); - int contentWidth = Math.min(this.width - 10, 480); - int contentLeft = (this.width - contentWidth) / 2; - int contentTop = 30; - int colGap = 4; - int colWidth = (contentWidth - colGap) / 2; - - int startIdx = currentPage * CARDS_PER_PAGE; - int endIdx = Math.min(startIdx + CARDS_PER_PAGE, cardEntries.size()); + int startIdx = currentPage * cardsPerPage; + int endIdx = Math.min(startIdx + cardsPerPage, cardEntries.size()); + tooltipStack = null; for (int i = startIdx; i < endIdx; i++) { int localIdx = i - startIdx; - int col = localIdx / CARDS_PER_COL; - int row = localIdx % CARDS_PER_COL; - int cardX = contentLeft + col * (colWidth + colGap); - int cardY = contentTop + row * (CARD_HEIGHT + CARD_PADDING); + int col = localIdx / cardsPerCol; + int row = localIdx % cardsPerCol; + int cardX = layoutContentLeft + col * (layoutColWidth + layoutColGap); + int cardY = layoutContentTop + row * (CARD_HEIGHT + CARD_PADDING); GroupCardEntry card = cardEntries.get(i); - drawCard(card, cardX, cardY, colWidth); + drawCard(card, cardX, cardY, layoutColWidth, mouseX, mouseY); } // Page counter if (totalPages > 1) { - int navY = contentTop + CARDS_PER_COL * (CARD_HEIGHT + CARD_PADDING) + 4; + int navY = layoutContentTop + cardsPerCol * (CARD_HEIGHT + CARD_PADDING) + 4; String pageText = (currentPage + 1) + "/" + totalPages; this.drawCenteredString(this.fontRenderer, pageText, this.width / 2, navY + 6, 0xAAAAAA); } // Empty state if (cardEntries.isEmpty()) { - this.drawCenteredString(this.fontRenderer, "No collapsible groups defined", this.width / 2, contentTop + 20, 0x888888); + this.drawCenteredString(this.fontRenderer, "No collapsible groups defined", this.width / 2, layoutContentTop + 20, 0x888888); } super.drawScreen(mouseX, mouseY, partialTicks); + + if (tooltipStack != null) { + renderToolTip(tooltipStack, mouseX, mouseY); + } } - private void drawCard(GroupCardEntry card, int x, int y, int width) { + private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, int mouseY) { // Card background int bgColor = card.isCustom ? 0x40336699 : 0x40444444; drawRect(x, y, x + width, y + CARD_HEIGHT, bgColor); @@ -311,30 +328,34 @@ private void drawCard(GroupCardEntry card, int x, int y, int width) { String countText = String.format(Translator.translateToLocal("jei.gui.collapsible.itemCount"), card.itemCount); this.fontRenderer.drawStringWithShadow(countText, x + 4, y + 16, 0xAAAAAA); - // Preview items + // Preview items — up to PREVIEW_ROWS rows of PREVIEW_COLS items each int previewY = y + 28; int previewX = x + 4; + int slotSize = PREVIEW_SIZE + 2; + int maxPreview = PREVIEW_COLS * PREVIEW_ROWS; RenderHelper.enableGUIStandardItemLighting(); GlStateManager.enableDepth(); - int count = Math.min(card.previewItems.size(), PREVIEW_COLS); + int count = Math.min(card.previewItems.size(), maxPreview); for (int i = 0; i < count; i++) { ItemStack stack = card.previewItems.get(i); - int itemX = previewX + i * (PREVIEW_SIZE + 2); - this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, itemX, previewY); + int itemX = previewX + (i % PREVIEW_COLS) * slotSize; + int itemY = previewY + (i / PREVIEW_COLS) * slotSize; + this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, itemX, itemY); + if (mouseX >= itemX && mouseX < itemX + PREVIEW_SIZE + && mouseY >= itemY && mouseY < itemY + PREVIEW_SIZE) { + tooltipStack = stack; + } } RenderHelper.disableStandardItemLighting(); GlStateManager.disableDepth(); - if (card.itemCount > PREVIEW_COLS) { - String moreText = "+" + (card.itemCount - PREVIEW_COLS); - int moreX = previewX + PREVIEW_COLS * (PREVIEW_SIZE + 2) + 2; - this.fontRenderer.drawStringWithShadow(moreText, moreX, previewY + 4, 0x888888); + if (card.itemCount > maxPreview) { + String moreText = "+" + (card.itemCount - maxPreview); + int moreX = previewX + PREVIEW_COLS * slotSize + 2; + int moreY = previewY + (PREVIEW_ROWS - 1) * slotSize + 4; + this.fontRenderer.drawStringWithShadow(moreText, moreX, moreY, 0x888888); } - - // ID text (small, dimmed) - String idText = "\u00A78" + card.id; - this.fontRenderer.drawStringWithShadow(idText, x + 4, y + CARD_HEIGHT - 12, 0x666666); } @Override @@ -362,7 +383,9 @@ protected void keyTyped(char typedChar, int keyCode) throws IOException { } /** - * Get up to 16 preview ItemStacks for a collapsible entry by scanning the ingredient list. + * Get up to PREVIEW_COLS * PREVIEW_ROWS preview ItemStacks for a collapsible entry. + * Non-ItemStack ingredients (e.g. EnchantmentData) are converted via the ingredient + * helper's getCheatItemStack so enchanted books render correctly. */ private List getPreviewItems(CollapsibleEntry entry) { List items = new ArrayList<>(); @@ -370,13 +393,14 @@ private List getPreviewItems(CollapsibleEntry entry) { return items; } IngredientFilter filter = Internal.getIngredientFilter(); - List ingredientList = filter.getIngredientList(); + List ingredientList = filter.getIngredientList(""); + int maxItems = PREVIEW_COLS * PREVIEW_ROWS; for (IIngredientListElement element : ingredientList) { if (entry.matches(element)) { - Object ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack) { - items.add((ItemStack) ingredient); - if (items.size() >= 16) { + ItemStack preview = toPreviewStack(element); + if (preview != null && !preview.isEmpty()) { + items.add(preview); + if (items.size() >= maxItems) { break; } } @@ -385,6 +409,21 @@ private List getPreviewItems(CollapsibleEntry entry) { return items; } + /** Converts any ingredient to a renderable ItemStack. Returns null if not possible. */ + @Nullable + private static ItemStack toPreviewStack(IIngredientListElement element) { + V ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + return (ItemStack) ingredient; + } + try { + IIngredientHelper helper = Internal.getIngredientRegistry().getIngredientHelper(ingredient); + return helper.getCheatItemStack(ingredient); + } catch (Exception e) { + return null; + } + } + /** * Count matched items for display. */ @@ -393,7 +432,7 @@ private int getMatchedItemCount(CollapsibleEntry entry) { return 0; } IngredientFilter filter = Internal.getIngredientFilter(); - List ingredientList = filter.getIngredientList(); + List ingredientList = filter.getIngredientList(""); int count = 0; for (IIngredientListElement element : ingredientList) { if (entry.matches(element)) { From 00ffcb63062fc58cbb561f04e5dd11884d60c066 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 04:35:08 -0400 Subject: [PATCH 09/48] Implement collapsed icon preview Render 8 x 3 rows of icons, if overflow, -1 from last row and print + overflow count. --- .../jei/render/CollapsedStackRenderer.java | 101 +++++++++++++++--- 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index 0185c6b59..388e365e2 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -9,6 +9,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.RenderItem; @@ -129,26 +130,94 @@ public void drawHighlight() { } public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { - List tooltip = new ArrayList<>(); - - // Group name and count - tooltip.add(TextFormatting.GOLD + collapsedStack.getDisplayName() + TextFormatting.GRAY + " (" + collapsedStack.size() + " items)"); - - // Show first few item names List> ingredients = collapsedStack.getIngredients(); - int previewCount = Math.min(ingredients.size(), 5); - for (int i = 0; i < previewCount; i++) { - tooltip.add(TextFormatting.GRAY + " " + ingredients.get(i).getDisplayName()); - } - if (ingredients.size() > previewCount) { - tooltip.add(TextFormatting.DARK_GRAY + " ..." + (ingredients.size() - previewCount) + " more"); + if (ingredients.isEmpty()) return; + + FontRenderer font = minecraft.fontRenderer; + final int COLS = 8; + final int SLOT = 18; // 16px icon + 1px padding each side + final int MAX_VISIBLE = COLS * 2 + 7; // 23 = rows of 8, 8, 7 + + int total = ingredients.size(); + int shown = Math.min(total, MAX_VISIBLE); + int overflow = total - shown; + int numRows = shown <= COLS ? 1 : shown <= COLS * 2 ? 2 : 3; + int gridCols = numRows > 1 ? COLS : shown; + int gridW = gridCols * SLOT; + int gridH = numRows * SLOT; + + String header = TextFormatting.GOLD + collapsedStack.getDisplayName() + + TextFormatting.GRAY + " (" + total + " items)"; + String hint = TextFormatting.YELLOW + Translator.translateToLocal("jei.tooltip.collapsed.expand"); + + int tw = Math.max(font.getStringWidth(header), gridW); + int th = 12 + gridH + 10; + + ScaledResolution sr = new ScaledResolution(minecraft); + int tx = mouseX + 12; + if (tx + tw + 6 > sr.getScaledWidth()) tx = mouseX - 16 - tw; + int ty = mouseY - 12; + if (ty + th + 4 > sr.getScaledHeight()) ty = sr.getScaledHeight() - th - 4; + if (ty < 4) ty = 4; + + GlStateManager.disableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + + // Draw tooltip background (MC-style dark purple box with gradient border) + final int z = 300; + int bg = 0xF0100010, bs = 0x505000FF, be = (bs & 0xFEFEFE) >> 1 | (bs & 0xFF000000); + GuiUtils.drawGradientRect(z, tx-3, ty-4, tx+tw+3, ty-3, bg, bg); + GuiUtils.drawGradientRect(z, tx-3, ty+th+3, tx+tw+3, ty+th+4, bg, bg); + GuiUtils.drawGradientRect(z, tx-3, ty-3, tx+tw+3, ty+th+3, bg, bg); + GuiUtils.drawGradientRect(z, tx-4, ty-3, tx-3, ty+th+3, bg, bg); + GuiUtils.drawGradientRect(z, tx+tw+3, ty-3, tx+tw+4, ty+th+3, bg, bg); + GuiUtils.drawGradientRect(z, tx-3, ty-2, tx-2, ty+th+2, bs, be); + GuiUtils.drawGradientRect(z, tx+tw+2, ty-2, tx+tw+3, ty+th+2, bs, be); + GuiUtils.drawGradientRect(z, tx-3, ty-3, tx+tw+3, ty-2, bs, bs); + GuiUtils.drawGradientRect(z, tx-3, ty+th+2, tx+tw+3, ty+th+3, be, be); + + // Title + font.drawStringWithShadow(header, tx, ty, -1); + + // Item icon grid + int itemsY = ty + 12; + GlStateManager.pushMatrix(); + GlStateManager.translate(0.0f, 0.0f, 300.0f); + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.enableDepth(); + RenderItem renderItem = minecraft.getRenderItem(); + for (int i = 0; i < shown; i++) { + IIngredientListElement element = ingredients.get(i); + int ix = tx + (i % COLS) * SLOT + 1; + int iy = itemsY + (i / COLS) * SLOT + 1; + Object ing = element.getIngredient(); + if (ing instanceof ItemStack) { + renderItem.renderItemAndEffectIntoGUI((ItemStack) ing, ix, iy); + } else { + try { renderIngredient(minecraft, ix, iy, element); } + catch (RuntimeException | LinkageError ignored) {} + } } + RenderHelper.disableStandardItemLighting(); + GlStateManager.popMatrix(); - tooltip.add(""); - tooltip.add(TextFormatting.YELLOW + Translator.translateToLocal("jei.tooltip.collapsed.expand")); + // "+N" overflow indicator in 8th slot of row 3 (only when there are hidden items) + GlStateManager.disableDepth(); + GlStateManager.disableLighting(); + if (overflow > 0) { + String overStr = "+" + overflow; + int ox = tx + 7 * SLOT + 2; + int oy = itemsY + 2 * SLOT + (SLOT - 8) / 2 + 1; + font.drawStringWithShadow(overStr, ox, oy, 0xAAAAAA); + } + font.drawStringWithShadow(hint, tx, itemsY + gridH + 2, -1); - FontRenderer fontRenderer = minecraft.fontRenderer; - TooltipRenderer.drawHoveringText(minecraft, tooltip, mouseX, mouseY, fontRenderer); + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableRescaleNormal(); } /** From f68ec93b9d2f946c69ae3d51768729b1df32b90e Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 05:46:37 -0400 Subject: [PATCH 10/48] Implement Drag Selection Simple drag selection Allowing duplicate items in multiple groups. Not sure why people would want this, but its how REI does it too. --- .../collapsible/GuiCustomGroupEditor.java | 53 ++++++++++++++++++- .../jei/ingredients/IngredientFilter.java | 1 - 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index cf17fac12..497702983 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -71,6 +71,11 @@ public class GuiCustomGroupEditor extends GuiScreen { // Cached selected stacks for the right panel private List selectedStacks = new ArrayList<>(); + // Drag-select state + private boolean isDragging = false; + private boolean dragAdding = false; + @Nullable private String lastDraggedUid = null; + public GuiCustomGroupEditor(GuiCollapsibleGroups parentScreen, CustomGroupsConfig.CustomGroup group) { this.parentScreen = parentScreen; this.group = group; @@ -437,7 +442,7 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx } } - // Left grid click: toggle item selection + // Left grid click: toggle item selection and start drag if (mouseButton == 0 && !filteredItems.isEmpty()) { int startIdx = leftPage * leftItemsPerPage; for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { @@ -449,7 +454,14 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx IIngredientListElement element = filteredItems.get(startIdx + i); Object ingredient = element.getIngredient(); if (ingredient instanceof ItemStack) { - toggleSelection((ItemStack) ingredient); + ItemStack stack = (ItemStack) ingredient; + try { + String uid = Internal.getStackHelper().getUniqueIdentifierForStack(stack); + dragAdding = !selectedUids.contains(uid); + isDragging = true; + lastDraggedUid = uid; + } catch (Exception ignored) {} + toggleSelection(stack); } return; } @@ -475,6 +487,43 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx super.mouseClicked(mouseX, mouseY, mouseButton); } + @Override + protected void mouseReleased(int mouseX, int mouseY, int state) { + super.mouseReleased(mouseX, mouseY, state); + isDragging = false; + lastDraggedUid = null; + } + + @Override + protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { + super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); + if (!isDragging || clickedMouseButton != 0 || filteredItems.isEmpty()) return; + int startIdx = leftPage * leftItemsPerPage; + for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { + int col = i % leftCols; + int row = i / leftCols; + int x = leftGridX + col * ITEM_SIZE; + int y = leftGridY + row * ITEM_SIZE; + if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { + Object ingredient = filteredItems.get(startIdx + i).getIngredient(); + if (ingredient instanceof ItemStack) { + try { + String uid = Internal.getStackHelper().getUniqueIdentifierForStack((ItemStack) ingredient); + if (!uid.equals(lastDraggedUid)) { + lastDraggedUid = uid; + if (dragAdding) { + if (selectedUids.add(uid)) updateSelectedStacks(); + } else { + if (selectedUids.remove(uid)) updateSelectedStacks(); + } + } + } catch (Exception ignored) {} + } + return; + } + } + } + private void toggleSelection(ItemStack stack) { try { StackHelper stackHelper = Internal.getStackHelper(); diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index cc4830c08..57a5a6ee9 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -314,7 +314,6 @@ private List collapse(List ingredientList) { } collapsed.addIngredient(element); matched = true; - break; // first match wins } } if (!matched) { From 9a448f9464f684d7a1d7d45fe25210f3eb29037b Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 06:21:59 -0400 Subject: [PATCH 11/48] Fix back button alignment --- .../mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index f15829faa..d4a474912 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -82,7 +82,7 @@ public void initGui() { computeLayout(); // Back button - this.buttonList.add(new GuiButton(BTN_BACK, 4, 4, 60, 20, + this.buttonList.add(new GuiButton(BTN_BACK, layoutContentLeft, 4, 60, 20, Translator.translateToLocal("jei.gui.collapsible.back"))); // New Group button From 3b67b1fe8999c0e27cf5f099607e398fce00fe06 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 20:10:15 -0400 Subject: [PATCH 12/48] Implement scrolling preview lists Changed the static list with overflow counter to a basic scrolling list. Scrollwheel and basic mouse drag support. --- .../collapsible/GuiCollapsibleGroups.java | 147 +++++++++++++++--- 1 file changed, 127 insertions(+), 20 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index d4a474912..1e72a2b61 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -34,6 +34,8 @@ public class GuiCollapsibleGroups extends GuiScreen { private static final int PREVIEW_SIZE = 16; private static final int PREVIEW_COLS = 8; private static final int PREVIEW_ROWS = 3; + // Maximum items fetched per card for the scrollable preview (20 scrollable rows) + private static final int PREVIEW_FETCH_MAX = PREVIEW_COLS * 20; // Dynamic layout — recomputed on each initGui() call so the screen adapts to GUI scale private int cardsPerCol; @@ -58,6 +60,11 @@ public class GuiCollapsibleGroups extends GuiScreen { private int totalPages = 1; @Nullable private ItemStack tooltipStack = null; + // Drag-to-scroll state for card preview boxes + private int dragCardAbsIdx = -1; + private int dragStartMouseY; + private int dragStartRow; + public GuiCollapsibleGroups(GuiScreen parentScreen) { this.parentScreen = parentScreen; } @@ -328,19 +335,26 @@ private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, String countText = String.format(Translator.translateToLocal("jei.gui.collapsible.itemCount"), card.itemCount); this.fontRenderer.drawStringWithShadow(countText, x + 4, y + 16, 0xAAAAAA); - // Preview items — up to PREVIEW_ROWS rows of PREVIEW_COLS items each - int previewY = y + 28; - int previewX = x + 4; + // Scrollable preview grid int slotSize = PREVIEW_SIZE + 2; - int maxPreview = PREVIEW_COLS * PREVIEW_ROWS; + int previewX = x + 4; + int previewY = y + 28; + int totalRows = (card.previewItems.size() + PREVIEW_COLS - 1) / PREVIEW_COLS; + int maxScrollRow = Math.max(0, totalRows - PREVIEW_ROWS); + card.previewScrollRow = Math.max(0, Math.min(card.previewScrollRow, maxScrollRow)); + int scrollRow = card.previewScrollRow; + + int firstItem = scrollRow * PREVIEW_COLS; + int lastItem = Math.min(card.previewItems.size(), (scrollRow + PREVIEW_ROWS) * PREVIEW_COLS); RenderHelper.enableGUIStandardItemLighting(); GlStateManager.enableDepth(); - int count = Math.min(card.previewItems.size(), maxPreview); - for (int i = 0; i < count; i++) { + for (int i = firstItem; i < lastItem; i++) { ItemStack stack = card.previewItems.get(i); - int itemX = previewX + (i % PREVIEW_COLS) * slotSize; - int itemY = previewY + (i / PREVIEW_COLS) * slotSize; + int visibleRow = (i / PREVIEW_COLS) - scrollRow; + int col = i % PREVIEW_COLS; + int itemX = previewX + col * slotSize; + int itemY = previewY + visibleRow * slotSize; this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, itemX, itemY); if (mouseX >= itemX && mouseX < itemX + PREVIEW_SIZE && mouseY >= itemY && mouseY < itemY + PREVIEW_SIZE) { @@ -350,11 +364,15 @@ private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, RenderHelper.disableStandardItemLighting(); GlStateManager.disableDepth(); - if (card.itemCount > maxPreview) { - String moreText = "+" + (card.itemCount - maxPreview); - int moreX = previewX + PREVIEW_COLS * slotSize + 2; - int moreY = previewY + (PREVIEW_ROWS - 1) * slotSize + 4; - this.fontRenderer.drawStringWithShadow(moreText, moreX, moreY, 0x888888); + // Scrollbar indicator + if (maxScrollRow > 0) { + int sbX = previewX + PREVIEW_COLS * slotSize + 2; + int sbY = previewY; + int sbH = PREVIEW_ROWS * slotSize; + drawRect(sbX, sbY, sbX + 3, sbY + sbH, 0x55FFFFFF); + int thumbH = Math.max(4, sbH * PREVIEW_ROWS / totalRows); + int thumbY = sbY + (sbH - thumbH) * scrollRow / maxScrollRow; + drawRect(sbX, thumbY, sbX + 3, thumbY + thumbH, 0xCCFFFFFF); } } @@ -363,16 +381,104 @@ public void handleMouseInput() throws IOException { super.handleMouseInput(); int scrollDelta = Mouse.getEventDWheel(); if (scrollDelta != 0) { - if (scrollDelta < 0 && currentPage < totalPages - 1) { - currentPage++; - rebuildPageButtons(); - } else if (scrollDelta > 0 && currentPage > 0) { - currentPage--; - rebuildPageButtons(); + int mouseX = Mouse.getEventX() * this.width / this.mc.displayWidth; + int mouseY = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; + + // Check if the cursor is inside any visible card's preview box + boolean handledByCard = false; + int slotSize = PREVIEW_SIZE + 2; + int startIdx = currentPage * cardsPerPage; + int endIdx = Math.min(startIdx + cardsPerPage, cardEntries.size()); + for (int i = startIdx; i < endIdx; i++) { + int localIdx = i - startIdx; + int col = localIdx / cardsPerCol; + int row = localIdx % cardsPerCol; + int cardX = layoutContentLeft + col * (layoutColWidth + layoutColGap); + int cardY = layoutContentTop + row * (CARD_HEIGHT + CARD_PADDING); + int previewX = cardX + 4; + int previewY = cardY + 28; + int previewW = PREVIEW_COLS * slotSize; + int previewH = PREVIEW_ROWS * slotSize; + if (mouseX >= previewX && mouseX < previewX + previewW + && mouseY >= previewY && mouseY < previewY + previewH) { + GroupCardEntry card = cardEntries.get(i); + int totalRows = (card.previewItems.size() + PREVIEW_COLS - 1) / PREVIEW_COLS; + int maxScrollRow = Math.max(0, totalRows - PREVIEW_ROWS); + if (maxScrollRow > 0) { + if (scrollDelta < 0) { + card.previewScrollRow = Math.min(card.previewScrollRow + 1, maxScrollRow); + } else { + card.previewScrollRow = Math.max(card.previewScrollRow - 1, 0); + } + handledByCard = true; + } + break; + } + } + + if (!handledByCard) { + if (scrollDelta < 0 && currentPage < totalPages - 1) { + currentPage++; + rebuildPageButtons(); + } else if (scrollDelta > 0 && currentPage > 0) { + currentPage--; + rebuildPageButtons(); + } } } } + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + super.mouseClicked(mouseX, mouseY, mouseButton); + if (mouseButton == 0) { + dragCardAbsIdx = -1; + int slotSize = PREVIEW_SIZE + 2; + int startIdx = currentPage * cardsPerPage; + int endIdx = Math.min(startIdx + cardsPerPage, cardEntries.size()); + for (int i = startIdx; i < endIdx; i++) { + int localIdx = i - startIdx; + int col = localIdx / cardsPerCol; + int row = localIdx % cardsPerCol; + int cardX = layoutContentLeft + col * (layoutColWidth + layoutColGap); + int cardY = layoutContentTop + row * (CARD_HEIGHT + CARD_PADDING); + int previewX = cardX + 4; + int previewY = cardY + 28; + int previewW = PREVIEW_COLS * slotSize; + int previewH = PREVIEW_ROWS * slotSize; + if (mouseX >= previewX && mouseX < previewX + previewW + && mouseY >= previewY && mouseY < previewY + previewH) { + dragCardAbsIdx = i; + dragStartMouseY = mouseY; + dragStartRow = cardEntries.get(i).previewScrollRow; + break; + } + } + } + } + + @Override + protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { + super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); + if (clickedMouseButton == 0 && dragCardAbsIdx >= 0 && dragCardAbsIdx < cardEntries.size()) { + int slotSize = PREVIEW_SIZE + 2; + GroupCardEntry card = cardEntries.get(dragCardAbsIdx); + int totalRows = (card.previewItems.size() + PREVIEW_COLS - 1) / PREVIEW_COLS; + int maxScrollRow = Math.max(0, totalRows - PREVIEW_ROWS); + // Dragging up (negative deltaY) scrolls down through items + int rowDelta = (dragStartMouseY - mouseY) / slotSize; + card.previewScrollRow = Math.max(0, Math.min(dragStartRow + rowDelta, maxScrollRow)); + } + } + + @Override + protected void mouseReleased(int mouseX, int mouseY, int state) { + super.mouseReleased(mouseX, mouseY, state); + if (state == 0) { + dragCardAbsIdx = -1; + } + } + @Override protected void keyTyped(char typedChar, int keyCode) throws IOException { if (keyCode == 1) { // ESC @@ -394,7 +500,7 @@ private List getPreviewItems(CollapsibleEntry entry) { } IngredientFilter filter = Internal.getIngredientFilter(); List ingredientList = filter.getIngredientList(""); - int maxItems = PREVIEW_COLS * PREVIEW_ROWS; + int maxItems = PREVIEW_FETCH_MAX; for (IIngredientListElement element : ingredientList) { if (entry.matches(element)) { ItemStack preview = toPreviewStack(element); @@ -457,6 +563,7 @@ private static class GroupCardEntry { boolean enabled; final List previewItems; final int itemCount; + int previewScrollRow = 0; GroupCardEntry(String id, String displayName, boolean isCustom, boolean enabled, List previewItems, int itemCount) { this.id = id; From e4d3f71c471d34c368a7f0f8ea8b509415642cc0 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 20:20:13 -0400 Subject: [PATCH 13/48] Searchable Group Names Implements searchable group names instead of only items within a group. --- .../jei/ingredients/IngredientFilter.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index 57a5a6ee9..d538bad7e 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -184,6 +184,9 @@ public List getIngredientList(String filterText) { filterText = Translator.toLowercaseWithLocale(filterText); if (!filterText.equals(filterCached)) { List> ingredientList = getIngredientListUncached(filterText); + if (!filterText.isEmpty() && Config.isCollapsibleGroupsEnabled()) { + ingredientList = withGroupNameMatches(ingredientList, filterText); + } ingredientListCached = Collections.unmodifiableList(ingredientList); collapsedListCached = collapse(ingredientListCached); filterCached = filterText; @@ -265,6 +268,49 @@ private List> getIngredientListUncached(String filterT .collect(Collectors.toList()); } + /** + * Augments a filtered ingredient list with any ingredients that belong to a group whose + * display name contains the filter text, but that weren't returned by the normal token + * search. This lets users find groups by name in the main search bar. + */ + @SuppressWarnings("unchecked") + private List> withGroupNameMatches( + List> baseList, String filterText) { + CollapsibleEntryRegistry registry = CollapsibleEntryRegistry.getInstance(); + List matchingGroups = new ArrayList<>(); + for (CollapsibleEntry entry : registry.getEntries()) { + if (Translator.toLowercaseWithLocale(entry.getDisplayName()).contains(filterText)) { + matchingGroups.add(entry); + } + } + for (CollapsibleEntry entry : registry.getCustomEntries()) { + if (Translator.toLowercaseWithLocale(entry.getDisplayName()).contains(filterText)) { + matchingGroups.add(entry); + } + } + if (matchingGroups.isEmpty()) { + return baseList; + } + // Use identity comparison so dedup works regardless of equals() implementation. + Set> seen = Collections.newSetFromMap(new IdentityHashMap<>()); + seen.addAll(baseList); + List> result = new ArrayList<>(baseList); + for (IIngredientListElement element : this.elementSearch.getAllIngredients()) { + if (!element.isVisible() || seen.contains(element)) { + continue; + } + for (CollapsibleEntry entry : matchingGroups) { + if (entry.matches(element)) { + result.add(element); + seen.add(element); + break; + } + } + } + result.sort(IngredientListElementComparator.INSTANCE); + return result; + } + /** * Converts a flat filtered ingredient list into a mixed list containing * both individual IIngredientListElement objects and CollapsedStack groups. From 7c0e3be51a11b1a6fa6e1644a13dcad30a534d64 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 20:32:19 -0400 Subject: [PATCH 14/48] Don't save group on new group. When a user presses "New Group" but goes back, the expected behavior is to cancel and not create a new group. Only save group on save. --- .../mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 1e72a2b61..e66897372 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -180,8 +180,6 @@ protected void actionPerformed(GuiButton button) throws IOException { CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); if (customGroupsConfig != null) { CustomGroupsConfig.CustomGroup newGroup = new CustomGroupsConfig.CustomGroup(newId, "New Group", new ArrayList<>()); - customGroupsConfig.addGroup(newGroup); - Internal.getCollapsibleEntryRegistry().recollectCustomEntries(); this.mc.displayGuiScreen(new GuiCustomGroupEditor(this, newGroup)); } return; From 20791b235bb5fecde7887706b53df2f47fa6aeee Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 22:14:17 -0400 Subject: [PATCH 15/48] Fancy group preview icons. Displays the first two items in a group, better indicating its a group. Changed the item count to a orange color, to better match the tooltip color and to stand out better. Decreased the size of the count. --- .../jei/render/CollapsedStackRenderer.java | 78 +++++++++++++------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index 388e365e2..67ce01e2b 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -57,7 +57,11 @@ public Rectangle getArea() { /** * Renders the collapsed group as a single slot. - * Draws a tinted background, the first item, and a count badge. + * For groups with 2+ items, mimics REI's stacked-card icon: each item is rendered at + * 0.75× scale (12 px), offset 4 px so both stay entirely within the 16×16 slot area. + * Back item (upper-right): screen origin (x+4, y+0), occupies (x+4..x+16, y..y+12) + * Front item (lower-left) : screen origin (x+0, y+4), occupies (x..x+12, y+4..y+16) + * Count badge is drawn at 0.75× scale in orange in the bottom-right corner. */ public void render(Minecraft minecraft) { List> ingredients = collapsedStack.getIngredients(); @@ -71,46 +75,70 @@ public void render(Minecraft minecraft) { // Draw background tint to visually distinguish collapsed groups GuiScreen.drawRect(x, y, x + 16, y + 16, COLLAPSED_BG_COLOR); - // Render the first item as the representative - IIngredientListElement firstElement = ingredients.get(0); - Object ingredient = firstElement.getIngredient(); - if (ingredient instanceof ItemStack) { - ItemStack itemStack = (ItemStack) ingredient; - RenderHelper.enableGUIStandardItemLighting(); - RenderItem renderItem = minecraft.getRenderItem(); - renderItem.renderItemAndEffectIntoGUI(itemStack, x, y); - RenderHelper.disableStandardItemLighting(); + if (ingredients.size() == 1) { + // Single item: render at full size + renderElementAt(minecraft, ingredients.get(0), x, y, 1.0f); } else { - try { - renderIngredient(minecraft, x, y, firstElement); - } catch (RuntimeException | LinkageError e) { - // Silently ignore render errors for collapsed preview - } + // 0.75 scale → 12 px icon. + // Back (upper-right): origin at (x+4, y+0) → occupies x+4..x+16, y..y+12 + // Front (lower-left) : origin at (x+0, y+4) → occupies x..x+12, y+4..y+16 + RenderItem renderItem = minecraft.getRenderItem(); + renderElementAt(minecraft, ingredients.get(1), x + 4, y + 0, 0.75f); // back + // Elevate zLevel so the front item's depth values are naturally in front of the + // back item's geometry. Using GL_LEQUAL (normal) keeps the front item's own + // internal face culling intact — GL_ALWAYS would break tile-entity models. + float prevZLevel = renderItem.zLevel; + renderItem.zLevel += 100; + renderElementAt(minecraft, ingredients.get(0), x + 0, y + 4, 0.75f); // front + renderItem.zLevel = prevZLevel; } - // Draw count badge in bottom-right + // Count badge: 0.75× scale, orange, right-aligned at the bottom of the slot int count = collapsedStack.size(); if (count > 1) { - String countStr = String.valueOf(count); FontRenderer fontRenderer = minecraft.fontRenderer; + String countStr = String.valueOf(count); GlStateManager.disableLighting(); GlStateManager.disableDepth(); GlStateManager.disableBlend(); - - // Draw count text with shadow, right-aligned in the slot + final float badgeScale = 0.75f; + // Convert desired screen position to scaled-coordinate space. + // Screen right edge: x+16 → scaled coord (x+16)/badgeScale + // Screen top of text: y+10 → scaled coord (y+10)/badgeScale int textWidth = fontRenderer.getStringWidth(countStr); - int textX = x + 17 - textWidth; - int textY = y + 9; - - fontRenderer.drawStringWithShadow(countStr, textX, textY, 0xFFFFFF); - + int scaledRight = (int) ((x + 16) / badgeScale); + int scaledTop = (int) ((y + 10) / badgeScale); + GlStateManager.pushMatrix(); + GlStateManager.scale(badgeScale, badgeScale, 1.0f); + fontRenderer.drawStringWithShadow(countStr, scaledRight - textWidth, scaledTop, 0xFFAA00); + GlStateManager.popMatrix(); GlStateManager.enableDepth(); } - // Draw a subtle border to indicate this is a collapsible group drawCollapsedBorder(x, y); } + /** + * Renders one ingredient at (x, y) at the given scale using the GL matrix stack. + * Delegates to renderItemAndEffectIntoGUI so all item types (2D, 3D, built-in) render correctly. + */ + private void renderElementAt(Minecraft minecraft, IIngredientListElement element, int x, int y, float scale) { + Object ingredient = element.getIngredient(); + try { + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y, 0); + GlStateManager.scale(scale, scale, scale); + if (ingredient instanceof ItemStack) { + minecraft.getRenderItem().renderItemAndEffectIntoGUI((ItemStack) ingredient, 0, 0); + } else { + renderIngredient(minecraft, 0, 0, element); + } + GlStateManager.popMatrix(); + } catch (RuntimeException | LinkageError ignored) { + GlStateManager.popMatrix(); + } + } + private void drawCollapsedBorder(int x, int y) { // Small triangle indicator in the top-left corner to show it's collapsible GlStateManager.disableLighting(); From 23bf17c4a3e2a0deeaf2cfad3a082f9fdda79593 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 22:20:19 -0400 Subject: [PATCH 16/48] Collapsed group lighting --- .../java/mezz/jei/render/IngredientListBatchRenderer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java index c2ecec313..d877ab901 100644 --- a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java @@ -478,7 +478,10 @@ protected void renderImpl(Minecraft minecraft) { slot.renderSlow(); } - // collapsed group rendering + // collapsed group rendering — lighting enabled once for all groups; each renderer + // assumes it is on and does not toggle it per item. + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.enableDepth(); for (CollapsedStackRenderer collapsed : renderCollapsed) { collapsed.render(minecraft); } From c7c930f92e35ef766f5130cb6e39d9c7783739bf Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 22:23:33 -0400 Subject: [PATCH 17/48] Cached baked models --- .../mezz/jei/render/IngredientListBatchRenderer.java | 2 +- .../java/mezz/jei/render/ItemStackFastRenderer.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java index d877ab901..22f76e167 100644 --- a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java @@ -248,7 +248,7 @@ protected void set(IngredientListSlot ingredientListSlot, IIngredientListEle } if (!bakedModel.isBuiltInRenderer() && !(itemStack.getItem() instanceof ISlowRenderItem)) { - ItemStackFastRenderer renderer = new ItemStackFastRenderer(itemStackElement); + ItemStackFastRenderer renderer = new ItemStackFastRenderer(itemStackElement, bakedModel); ingredientListSlot.setIngredientRenderer(renderer); if (bakedModel.isGui3d()) { renderItems3d.add(renderer); diff --git a/src/main/java/mezz/jei/render/ItemStackFastRenderer.java b/src/main/java/mezz/jei/render/ItemStackFastRenderer.java index e39101136..e0f3f3c03 100644 --- a/src/main/java/mezz/jei/render/ItemStackFastRenderer.java +++ b/src/main/java/mezz/jei/render/ItemStackFastRenderer.java @@ -6,7 +6,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.ItemModelMesher; import net.minecraft.client.renderer.RenderItem; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; @@ -23,8 +22,12 @@ public class ItemStackFastRenderer extends IngredientRenderer { private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); - public ItemStackFastRenderer(IIngredientListElement itemStackElement) { + // Pre-computed at list-population time so getBakedModel() is a free field read every frame. + private final IBakedModel cachedModel; + + public ItemStackFastRenderer(IIngredientListElement itemStackElement, IBakedModel model) { super(itemStackElement); + this.cachedModel = model; } public void renderItemAndEffectIntoGUI() { @@ -36,10 +39,7 @@ public void renderItemAndEffectIntoGUI() { } private IBakedModel getBakedModel() { - ItemModelMesher itemModelMesher = Minecraft.getMinecraft().getRenderItem().getItemModelMesher(); - ItemStack itemStack = element.getIngredient(); - IBakedModel bakedModel = itemModelMesher.getItemModel(itemStack); - return bakedModel.getOverrides().handleItemState(bakedModel, itemStack, null, null); + return cachedModel; } private void uncheckedRenderItemAndEffectIntoGUI() { From 0ee24b944cf0ba2efae57f9185cf822bf5beedd1 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 13 Mar 2026 22:30:47 -0400 Subject: [PATCH 18/48] Fix tooltip min size to hint width --- src/main/java/mezz/jei/render/CollapsedStackRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index 67ce01e2b..bd14c48de 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -178,7 +178,7 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { + TextFormatting.GRAY + " (" + total + " items)"; String hint = TextFormatting.YELLOW + Translator.translateToLocal("jei.tooltip.collapsed.expand"); - int tw = Math.max(font.getStringWidth(header), gridW); + int tw = Math.max(Math.max(font.getStringWidth(header), font.getStringWidth(hint)), gridW); int th = 12 + gridH + 10; ScaledResolution sr = new ScaledResolution(minecraft); From 5468ca0ca6cb1e6737e19cba4278f57eb89f2e2a Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 14 Mar 2026 00:23:40 -0400 Subject: [PATCH 19/48] Implement Collapse on Close and Default Action Implement collapse on close, when HEI is closed collapse all currently open groups. Implement Default Action, user selectable single click action, default to the more user friendly single click to expand group. Inverts Alt+Click action. Fixed tooltips for closing expanded groups. --- src/main/java/mezz/jei/config/Config.java | 20 ++++++++++++++++++ .../java/mezz/jei/config/ConfigValues.java | 2 ++ .../java/mezz/jei/gui/GuiEventHandler.java | 10 +++++++++ .../mezz/jei/gui/overlay/IngredientGrid.java | 21 ++++++++++++++++--- .../jei/render/CollapsedStackRenderer.java | 7 ++++++- .../mezz/jei/render/IngredientRenderer.java | 8 +++++++ .../mezz/jei/util/CollapsedClickAction.java | 12 +++++++++++ 7 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/main/java/mezz/jei/util/CollapsedClickAction.java diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index 601638482..447fcae5b 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -16,6 +16,7 @@ import mezz.jei.startup.ForgeModIdHelper; import mezz.jei.startup.IModIdHelper; import mezz.jei.util.GiveMode; +import mezz.jei.util.CollapsedClickAction; import mezz.jei.util.Log; import mezz.jei.util.Translator; import net.minecraft.init.Items; @@ -96,6 +97,14 @@ public static boolean isCollapsibleGroupsEnabled() { return values.collapsibleGroupsEnabled; } + public static boolean isCollapseOnClose() { + return values.collapseOnClose; + } + + public static CollapsedClickAction getCollapsedClickAction() { + return values.collapsedClickAction; + } + @Nullable public static CustomGroupsConfig getCustomGroupsConfig() { return customGroupsConfig; @@ -573,6 +582,17 @@ private static boolean syncConfig() { } } + values.collapseOnClose = config.getBoolean(CATEGORY_COLLAPSIBLE, "collapseOnClose", defaultValues.collapseOnClose); + + values.collapsedClickAction = config.getEnum("collapsedClickAction", CATEGORY_COLLAPSIBLE, defaultValues.collapsedClickAction, CollapsedClickAction.values()); + + // Explicit property order so the GUI shows collapsibleGroupsEnabled first, then collapseOnClose. + java.util.List collapsibleOrder = new java.util.ArrayList<>(); + collapsibleOrder.add("collapsibleGroupsEnabled"); + collapsibleOrder.add("collapseOnClose"); + collapsibleOrder.add("collapsedClickAction"); + config.setCategoryPropertyOrder(CATEGORY_COLLAPSIBLE, collapsibleOrder); + { String[] disabledGroupsArray = config.getStringList("disabledGroups", CATEGORY_COLLAPSIBLE, new String[]{}); Property disabledProp = config.get(CATEGORY_COLLAPSIBLE, "disabledGroups", new String[]{}); diff --git a/src/main/java/mezz/jei/config/ConfigValues.java b/src/main/java/mezz/jei/config/ConfigValues.java index 2eb04c70a..3b94fcc54 100644 --- a/src/main/java/mezz/jei/config/ConfigValues.java +++ b/src/main/java/mezz/jei/config/ConfigValues.java @@ -58,5 +58,7 @@ public class ConfigValues { // collapsible groups public boolean collapsibleGroupsEnabled = true; + public boolean collapseOnClose = false; + public mezz.jei.util.CollapsedClickAction collapsedClickAction = mezz.jei.util.CollapsedClickAction.OPEN_GROUP; public Set disabledGroups = new HashSet<>(); } diff --git a/src/main/java/mezz/jei/gui/GuiEventHandler.java b/src/main/java/mezz/jei/gui/GuiEventHandler.java index c4ecafd46..94f92cd97 100644 --- a/src/main/java/mezz/jei/gui/GuiEventHandler.java +++ b/src/main/java/mezz/jei/gui/GuiEventHandler.java @@ -1,7 +1,9 @@ package mezz.jei.gui; +import mezz.jei.Internal; import mezz.jei.config.Config; import mezz.jei.config.OverlayToggleEvent; +import mezz.jei.ingredients.CollapsibleEntryRegistry; import mezz.jei.gui.ghost.GhostIngredientDragManager; import mezz.jei.gui.overlay.IngredientListOverlay; import mezz.jei.gui.overlay.bookmarks.LeftAreaDispatcher; @@ -56,10 +58,18 @@ public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) { @SubscribeEvent public void onGuiOpen(GuiOpenEvent event) { + boolean wasDisplayed = ingredientListOverlay.isListDisplayed(); GuiScreen gui = event.getGui(); ingredientListOverlay.updateScreen(gui, false); leftAreaDispatcher.updateScreen(gui, false); ghostIngredientDragManager.updateScreen(gui, false); + if (wasDisplayed && !ingredientListOverlay.isListDisplayed() && Config.isCollapseOnClose() + && Internal.hasIngredientFilter()) { + CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + registry.getEntries().forEach(e -> e.setExpanded(false)); + registry.getCustomEntries().forEach(e -> e.setExpanded(false)); + Internal.getIngredientFilter().notifyCollapsedStateChanged(); + } } @SubscribeEvent diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java index acb8d4e75..72ec8c9a6 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java @@ -20,6 +20,7 @@ import mezz.jei.render.IngredientRenderer; import mezz.jei.runtime.JeiRuntime; import mezz.jei.util.GiveMode; +import mezz.jei.util.CollapsedClickAction; import mezz.jei.util.MathUtil; import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; @@ -141,7 +142,14 @@ public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { } else { IngredientRenderer hovered = guiIngredientSlots.getHovered(mouseX, mouseY); if (hovered != null) { - hovered.drawTooltip(minecraft, mouseX, mouseY); + CollapsedStack expandedGroup = guiIngredientSlots.getExpandedCollapsedGroupAt(mouseX, mouseY); + if (expandedGroup != null) { + String hint = net.minecraft.util.text.TextFormatting.YELLOW + + mezz.jei.util.Translator.translateToLocal("jei.tooltip.collapsed.collapse"); + hovered.drawTooltip(minecraft, mouseX, mouseY, java.util.Collections.singletonList(hint)); + } else { + hovered.drawTooltip(minecraft, mouseX, mouseY); + } } } } @@ -183,14 +191,21 @@ public boolean isMouseOver(int mouseX, int mouseY) { public boolean handleMouseClicked(int mouseX, int mouseY) { if (isMouseOver(mouseX, mouseY)) { - // Alt+Click toggles collapsible groups - if (GuiScreen.isAltKeyDown()) { + boolean firstItemMode = Config.getCollapsedClickAction() == CollapsedClickAction.FIRST_ITEM; + boolean altDown = GuiScreen.isAltKeyDown(); + // OPEN_GROUP: plain click expands a collapsed icon; alt+click falls through (first item). + // FIRST_ITEM: alt+click expands a collapsed icon; plain click falls through (first item). + boolean expandKeyDown = firstItemMode ? altDown : !altDown; + if (expandKeyDown) { CollapsedStackRenderer collapsedHovered = guiIngredientSlots.getHoveredCollapsed(mouseX, mouseY); if (collapsedHovered != null) { collapsedHovered.getCollapsedStack().toggleExpanded(); Internal.getIngredientFilter().notifyCollapsedStateChanged(); return true; } + } + // Alt+Click on any item inside an expanded group always collapses it. + if (altDown) { CollapsedStack expandedHovered = guiIngredientSlots.getExpandedCollapsedGroupAt(mouseX, mouseY); if (expandedHovered != null) { expandedHovered.toggleExpanded(); diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index bd14c48de..c0f5400c1 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -176,7 +176,12 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { String header = TextFormatting.GOLD + collapsedStack.getDisplayName() + TextFormatting.GRAY + " (" + total + " items)"; - String hint = TextFormatting.YELLOW + Translator.translateToLocal("jei.tooltip.collapsed.expand"); + // In OPEN_GROUP mode, alt+click uses first item; show that as the hint. + // In FIRST_ITEM mode, alt+click expands; show that instead. + String hint = TextFormatting.YELLOW + Translator.translateToLocal( + mezz.jei.config.Config.getCollapsedClickAction() == mezz.jei.util.CollapsedClickAction.OPEN_GROUP + ? "jei.tooltip.collapsed.expand.firstItem" + : "jei.tooltip.collapsed.expand"); int tw = Math.max(Math.max(font.getStringWidth(header), font.getStringWidth(hint)), gridW); int th = 12 + gridH + 10; diff --git a/src/main/java/mezz/jei/render/IngredientRenderer.java b/src/main/java/mezz/jei/render/IngredientRenderer.java index 84653b234..f33773637 100644 --- a/src/main/java/mezz/jei/render/IngredientRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientRenderer.java @@ -85,9 +85,17 @@ public void drawHighlight() { } public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { + drawTooltip(minecraft, mouseX, mouseY, null); + } + + public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY, List extraLines) { T ingredient = element.getIngredient(); IIngredientRenderer ingredientRenderer = element.getIngredientRenderer(); List tooltip = getTooltip(minecraft, element); + if (extraLines != null && !extraLines.isEmpty()) { + tooltip = new ArrayList<>(tooltip); + tooltip.addAll(extraLines); + } FontRenderer fontRenderer = ingredientRenderer.getFontRenderer(minecraft, ingredient); IIngredientHelper ingredientHelper = element.getIngredientHelper(); diff --git a/src/main/java/mezz/jei/util/CollapsedClickAction.java b/src/main/java/mezz/jei/util/CollapsedClickAction.java new file mode 100644 index 000000000..b32636474 --- /dev/null +++ b/src/main/java/mezz/jei/util/CollapsedClickAction.java @@ -0,0 +1,12 @@ +package mezz.jei.util; + +/** + * Controls what a plain left-click on a collapsed group slot does. + * + * OPEN_GROUP — click opens/expands the group; Alt+Click applies the action to the first item. + * FIRST_ITEM — click applies the action to the first item in the group; Alt+Click expands/collapses. + */ +public enum CollapsedClickAction { + OPEN_GROUP, + FIRST_ITEM +} From b983d54ecfb3eca779df81eda7b53161efabba49 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 14 Mar 2026 00:25:08 -0400 Subject: [PATCH 20/48] Translations All translations were machine generated and cannot be validated with confidence. Take it with the grain of salt it deserves or leave it. --- src/main/resources/assets/jei/lang/ar_sa.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/bg_bg.lang | 31 +++++++++++++++++ src/main/resources/assets/jei/lang/cs_cz.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/de_de.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/el_gr.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/en_au.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/en_us.lang | 11 +++++-- src/main/resources/assets/jei/lang/es_es.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/fi_fi.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/fr_fr.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/he_il.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/it_it.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/ja_jp.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/ko_kr.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/lt_lt.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/nb_no.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/pl_pl.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/pt_br.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/ru_ru.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/sv_se.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/tr_tr.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/uk_ua.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/zh_cn.lang | 33 ++++++++++++++++++- src/main/resources/assets/jei/lang/zh_tw.lang | 33 ++++++++++++++++++- 24 files changed, 744 insertions(+), 24 deletions(-) diff --git a/src/main/resources/assets/jei/lang/ar_sa.lang b/src/main/resources/assets/jei/lang/ar_sa.lang index 364057ddd..7d40f1b11 100644 --- a/src/main/resources/assets/jei/lang/ar_sa.lang +++ b/src/main/resources/assets/jei/lang/ar_sa.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=إخفاء زر الإشارة المرجعية في الزاوية السفلية اليسرى -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=ما إذا كان سيتم إخفاء زر الإشارة المرجعية في الزاوية السفلية اليسرى \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=ما إذا كان سيتم إخفاء زر الإشارة المرجعية في الزاوية السفلية اليسرى + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+انقر للتوسيع +jei.tooltip.collapsed.expand.firstItem=Alt+انقر للعنصر الأول +jei.tooltip.collapsed.collapse=Alt+انقر للطي + +config.jei.collapsible=المجموعات القابلة للطي +config.jei.collapsible.comment=خيارات لطي مجموعات المكونات في خانة واحدة في قائمة المكونات. +config.jei.collapsible.collapsibleGroupsEnabled=تفعيل المجموعات القابلة للطي +config.jei.collapsible.collapsibleGroupsEnabled.comment=عند التفعيل، يتم طي مجموعات المكونات ذات الصلة (الجرعات، الكتب المسحورة، إلخ) في خانة واحدة. Alt+انقر على مجموعة لتوسيعها أو طيها. +config.jei.collapsible.collapseOnClose=الطي عند الإغلاق +config.jei.collapsible.collapseOnClose.comment=طي المجموعات المفتوحة حالياً عند إغلاق HEI. +config.jei.collapsible.collapsedClickAction=إجراء النقر الافتراضي +config.jei.collapsible.collapsedClickAction.comment=يبدل إجراء النقرة الواحدة، يصبح الخيار الآخر هو إجراء Alt+النقر. فتح المجموعة يفتح المجموعة. العنصر الأول يطبق الإجراء على أول عنصر في المجموعة. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=فتح المجموعة +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=العنصر الأول + +# Group Management GUI +jei.gui.collapsible.title=إدارة المجموعات +jei.gui.collapsible.back=رجوع +jei.gui.collapsible.newGroup=مجموعة جديدة +jei.gui.collapsible.title.tooltip=تفعيل/تعطيل المجموعات المدمجة وإنشاء مجموعات مخصصة. +jei.gui.collapsible.enabled=مفعّل +jei.gui.collapsible.disabled=معطّل +jei.gui.collapsible.customGroup=مخصص +jei.gui.collapsible.defaultGroup=افتراضي +jei.gui.collapsible.itemCount=%d عناصر +jei.gui.collapsible.editor.title=العناصر المحددة +jei.gui.collapsible.editor.name=الاسم +jei.gui.collapsible.editor.save=حفظ +jei.gui.collapsible.editor.selected=%d محدد \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/bg_bg.lang b/src/main/resources/assets/jei/lang/bg_bg.lang index 3e679329c..9a65cf31f 100644 --- a/src/main/resources/assets/jei/lang/bg_bg.lang +++ b/src/main/resources/assets/jei/lang/bg_bg.lang @@ -120,3 +120,34 @@ description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Cir config.jei.misc.hideBottomLeftCornerBookmarkButton=Скрий бутона за отметка в долния ляв ъгъл config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Дали да се скрие бутона за отметка в долния ляв ъгъл +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Клик за разгъване +jei.tooltip.collapsed.expand.firstItem=Alt+Клик за първия предмет +jei.tooltip.collapsed.collapse=Alt+Клик за свиване + +config.jei.collapsible=Свиваеми групи +config.jei.collapsible.comment=Опции за свиване на групи от съставки в един слот в списъка със съставки. +config.jei.collapsible.collapsibleGroupsEnabled=Активиране на свиваеми групи +config.jei.collapsible.collapsibleGroupsEnabled.comment=Когато е активирано, групи от свързани съставки (отвари, омагьосани книги и др.) се свиват в един слот. Alt+Клик върху група, за да я разгънете или свиете. +config.jei.collapsible.collapseOnClose=Свиване при затваряне +config.jei.collapsible.collapseOnClose.comment=Свиване на текущо отворените групи при затваряне на HEI. +config.jei.collapsible.collapsedClickAction=Действие при клик по подразбиране +config.jei.collapsible.collapsedClickAction.comment=Разменя действието при единично кликване, другата опция става действието при Alt+Клик. Отвори групата отваря групата. Първият предмет прилага действието към първия предмет в групата. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Отвори групата +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Първият предмет + +# Group Management GUI +jei.gui.collapsible.title=Управление на групи +jei.gui.collapsible.back=Назад +jei.gui.collapsible.newGroup=Нова група +jei.gui.collapsible.title.tooltip=Активиране/деактивиране на вградени групи и създаване на потребителски групи. +jei.gui.collapsible.enabled=Активирано +jei.gui.collapsible.disabled=Деактивирано +jei.gui.collapsible.customGroup=Потребителска +jei.gui.collapsible.defaultGroup=По подразбиране +jei.gui.collapsible.itemCount=%d предмети +jei.gui.collapsible.editor.title=Избрани предмети +jei.gui.collapsible.editor.name=Название +jei.gui.collapsible.editor.save=Запазване +jei.gui.collapsible.editor.selected=%d избрани + diff --git a/src/main/resources/assets/jei/lang/cs_cz.lang b/src/main/resources/assets/jei/lang/cs_cz.lang index 1a4c9c3cb..0141c8c94 100644 --- a/src/main/resources/assets/jei/lang/cs_cz.lang +++ b/src/main/resources/assets/jei/lang/cs_cz.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Kliknutí na dveře změní jejich status a jejich description.jei.wooden.door.3=Dřevěné dveře mohou být otevřeny/zavřeny pomocí Redstone Obvodů. config.jei.misc.hideBottomLeftCornerBookmarkButton=Skrýt tlačítko záložky v levém dolním rohu -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Zda skrýt tlačítko záložky v levém dolním rohu \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Zda skrýt tlačítko záložky v levém dolním rohu + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Klik pro rozbalení +jei.tooltip.collapsed.expand.firstItem=Alt+Klik pro první předmět +jei.tooltip.collapsed.collapse=Alt+Klik pro sbalení + +config.jei.collapsible=Sbalitelné skupiny +config.jei.collapsible.comment=Možnosti pro sbalení skupin ingrediencí do jednoho slotu v seznamu ingrediencí. +config.jei.collapsible.collapsibleGroupsEnabled=Povolit sbalitelné skupiny +config.jei.collapsible.collapsibleGroupsEnabled.comment=Pokud je povoleno, skupiny souvisejících ingrediencí (lektvary, očarované knihy atd.) jsou sbaleny do jednoho slotu. Alt+Klik na skupinu pro rozbalení nebo sbalení. +config.jei.collapsible.collapseOnClose=Sbalit při zavření +config.jei.collapsible.collapseOnClose.comment=Sbalí aktuálně otevřené skupiny při zavření HEI. +config.jei.collapsible.collapsedClickAction=Výchozí akce kliknutí +config.jei.collapsible.collapsedClickAction.comment=Zamění akci jednoduchého kliknutí, druhá možnost se stane akcí Alt+Klik. Otevřít skupinu otevře skupinu. První předmět aplikuje akci na první předmět ve skupině. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Otevřít skupinu +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=První předmět + +# Group Management GUI +jei.gui.collapsible.title=Správa skupin +jei.gui.collapsible.back=Zpět +jei.gui.collapsible.newGroup=Nová skupina +jei.gui.collapsible.title.tooltip=Povolení/zakázání vestavěných skupin a vytváření uživatelských skupin. +jei.gui.collapsible.enabled=Povoleno +jei.gui.collapsible.disabled=Zakázáno +jei.gui.collapsible.customGroup=Vlastní +jei.gui.collapsible.defaultGroup=Výchozí +jei.gui.collapsible.itemCount=%d předmětů +jei.gui.collapsible.editor.title=Vybrané předměty +jei.gui.collapsible.editor.name=Název +jei.gui.collapsible.editor.save=Uložit +jei.gui.collapsible.editor.selected=%d vybráno diff --git a/src/main/resources/assets/jei/lang/de_de.lang b/src/main/resources/assets/jei/lang/de_de.lang index f8dbc3429..4ea8e9b10 100644 --- a/src/main/resources/assets/jei/lang/de_de.lang +++ b/src/main/resources/assets/jei/lang/de_de.lang @@ -151,4 +151,35 @@ config.jei.interface.defaultFluidContainerItem=Standard-Flüssigkeitsbehälter config.jei.interface.defaultFluidContainerItem.comment=Voreingestelltes Item, der zum Auffüllen von Flüssigkeiten verwendet wird, wenn im Menü auf eine Flüssigkeitszutat geklickt wird. config.jei.misc.hideBottomLeftCornerBookmarkButton=Lesezeichen-Button in der unteren linken Ecke ausblenden -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ob der Lesezeichen-Button in der unteren linken Ecke ausgeblendet werden soll \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ob der Lesezeichen-Button in der unteren linken Ecke ausgeblendet werden soll + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Klick zum Aufklappen +jei.tooltip.collapsed.expand.firstItem=Alt+Klick für erstes Element +jei.tooltip.collapsed.collapse=Alt+Klick zum Zuklappen + +config.jei.collapsible=Einklappbare Gruppen +config.jei.collapsible.comment=Optionen zum Einklappen von Zutatengruppen in einen einzelnen Slot in der Zutatenliste. +config.jei.collapsible.collapsibleGroupsEnabled=Einklappbare Gruppen aktivieren +config.jei.collapsible.collapsibleGroupsEnabled.comment=Wenn aktiviert, werden Gruppen verwandter Zutaten (Tränke, verzauberte Bücher usw.) in einen einzelnen Slot zusammengeklappt. Alt+Klick auf eine Gruppe zum Auf- oder Zuklappen. +config.jei.collapsible.collapseOnClose=Beim Schließen einklappen +config.jei.collapsible.collapseOnClose.comment=Aktuell geöffnete Gruppen beim Schließen von HEI einklappen. +config.jei.collapsible.collapsedClickAction=Standard-Klickaktion +config.jei.collapsible.collapsedClickAction.comment=Wechselt die Einzelklick-Aktion, die andere Option wird zur Alt+Klick-Aktion. Gruppe öffnen öffnet die Gruppe. Erstes Element wendet die Aktion auf das erste Element der Gruppe an. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Gruppe öffnen +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Erstes Element + +# Group Management GUI +jei.gui.collapsible.title=Gruppen verwalten +jei.gui.collapsible.back=Zurück +jei.gui.collapsible.newGroup=Neue Gruppe +jei.gui.collapsible.title.tooltip=Integrierte Gruppen aktivieren/deaktivieren und benutzerdefinierte Gruppen erstellen. +jei.gui.collapsible.enabled=Aktiviert +jei.gui.collapsible.disabled=Deaktiviert +jei.gui.collapsible.customGroup=Benutzerdefiniert +jei.gui.collapsible.defaultGroup=Standard +jei.gui.collapsible.itemCount=%d Elemente +jei.gui.collapsible.editor.title=Ausgewählte Elemente +jei.gui.collapsible.editor.name=Name +jei.gui.collapsible.editor.save=Speichern +jei.gui.collapsible.editor.selected=%d ausgewählt diff --git a/src/main/resources/assets/jei/lang/el_gr.lang b/src/main/resources/assets/jei/lang/el_gr.lang index 31aca95a7..f2d701998 100644 --- a/src/main/resources/assets/jei/lang/el_gr.lang +++ b/src/main/resources/assets/jei/lang/el_gr.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Απόκρυψη Κουμπιού Σελιδοδείκτη στην Κάτω Αριστερή Γωνία -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Αν θα αποκρύπτεται το κουμπί σελιδοδείκτη στην κάτω αριστερή γωνία \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Αν θα αποκρύπτεται το κουμπί σελιδοδείκτη στην κάτω αριστερή γωνία + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Κλικ για ανάπτυξη +jei.tooltip.collapsed.expand.firstItem=Alt+Κλικ για πρώτο αντικείμενο +jei.tooltip.collapsed.collapse=Alt+Κλικ για σύμπτυξη + +config.jei.collapsible=Πτυσσόμενες Ομάδες +config.jei.collapsible.comment=Επιλογές για σύμπτυξη ομάδων συστατικών σε μία θέση στη λίστα συστατικών. +config.jei.collapsible.collapsibleGroupsEnabled=Ενεργοποίηση πτυσσόμενων ομάδων +config.jei.collapsible.collapsibleGroupsEnabled.comment=Όταν ενεργοποιηθεί, οι ομάδες σχετικών συστατικών (φίλτρα, μαγεμένα βιβλία κ.λπ.) συμπτύσσονται σε μία θέση. Alt+Κλικ σε μια ομάδα για ανάπτυξη ή σύμπτυξη. +config.jei.collapsible.collapseOnClose=Σύμπτυξη κατά το κλείσιμο +config.jei.collapsible.collapseOnClose.comment=Σύμπτυξη των τρεχόντων ανοιχτών ομάδων όταν κλείνει το HEI. +config.jei.collapsible.collapsedClickAction=Προεπιλεγμένη ενέργεια κλικ +config.jei.collapsible.collapsedClickAction.comment=Εναλλάσσει την ενέργεια μονού κλικ, η άλλη επιλογή γίνεται η ενέργεια Alt+Κλικ. Άνοιγμα Ομάδας ανοίγει την ομάδα. Πρώτο Αντικείμενο εφαρμόζει την ενέργεια στο πρώτο αντικείμενο της ομάδας. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Άνοιγμα Ομάδας +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Πρώτο Αντικείμενο + +# Group Management GUI +jei.gui.collapsible.title=Διαχείριση ομάδων +jei.gui.collapsible.back=Πίσω +jei.gui.collapsible.newGroup=Νέα ομάδα +jei.gui.collapsible.title.tooltip=Ενεργοποίηση/απενεργοποίηση ενσωματωμένων ομάδων και δημιουργία ομάδων χρήστη. +jei.gui.collapsible.enabled=Ενεργοποιημένο +jei.gui.collapsible.disabled=Απενεργοποιημένο +jei.gui.collapsible.customGroup=Προσαρμοσμένη +jei.gui.collapsible.defaultGroup=Προεπιλογή +jei.gui.collapsible.itemCount=%d αντικείμενα +jei.gui.collapsible.editor.title=Επιλεγμένα αντικείμενα +jei.gui.collapsible.editor.name=Όνομα +jei.gui.collapsible.editor.save=Αποθήκευση +jei.gui.collapsible.editor.selected=%d επιλεγμένα diff --git a/src/main/resources/assets/jei/lang/en_au.lang b/src/main/resources/assets/jei/lang/en_au.lang index 5ca7b147f..60c05eafa 100644 --- a/src/main/resources/assets/jei/lang/en_au.lang +++ b/src/main/resources/assets/jei/lang/en_au.lang @@ -119,4 +119,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Hide Bottom-Left Corner Bookmark Button -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Whether to hide the bottom-left corner bookmark button \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Whether to hide the bottom-left corner bookmark button + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Click to expand +jei.tooltip.collapsed.expand.firstItem=Alt+Click for first item +jei.tooltip.collapsed.collapse=Alt+Click to collapse + +config.jei.collapsible=Collapsible Groups +config.jei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. +config.jei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups +config.jei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. +config.jei.collapsible.collapseOnClose=Collapse on Close +config.jei.collapsible.collapseOnClose.comment=Collapse currently opened groups when HEI is closed. +config.jei.collapsible.collapsedClickAction=Default Collapsed Action +config.jei.collapsible.collapsedClickAction.comment=Swaps the single click action, The other option becomes the Alt+Click action. Open Group opens the group. First Item applies the action to the first item in the group. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Open Group +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=First Item + +# Group Management GUI +jei.gui.collapsible.title=Manage Groups +jei.gui.collapsible.back=Back +jei.gui.collapsible.newGroup=New Group +jei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user defined groups. +jei.gui.collapsible.enabled=Enabled +jei.gui.collapsible.disabled=Disabled +jei.gui.collapsible.customGroup=Custom +jei.gui.collapsible.defaultGroup=Default +jei.gui.collapsible.itemCount=%d items +jei.gui.collapsible.editor.title=Selected Items +jei.gui.collapsible.editor.name=Name +jei.gui.collapsible.editor.save=Save +jei.gui.collapsible.editor.selected=%d selected diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index 65b5d612b..a0028c189 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -23,8 +23,9 @@ jei.tooltip.bookmarks.usage.key=Hover over an ingredient and press "%s" to bookm jei.tooltip.bookmarks.not.enough.space=There is not enough space to display bookmarks here. # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Click to expand -jei.tooltip.collapsed.collapse=Alt+Click to collapse +jei.tooltip.collapsed.expand=Alt+Click to Expand +jei.tooltip.collapsed.expand.firstItem=Alt+Click for First Item +jei.tooltip.collapsed.collapse=Alt+Click to Collapse # Error Tooltips jei.tooltip.error.recipe.transfer.missing=Missing Items @@ -190,6 +191,12 @@ config.jei.collapsible=Collapsible Groups config.jei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. config.jei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups config.jei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. +config.jei.collapsible.collapseOnClose=Collapse on Close +config.jei.collapsible.collapseOnClose.comment=Collapse currently opened groups when HEI is closed. +config.jei.collapsible.collapsedClickAction=Default Collapsed Action +config.jei.collapsible.collapsedClickAction.comment=Swaps the single click action, The other option becomes the Alt+Click action. Open Group opens the group. First Item applies the action to the first item in the group. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Open Group +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=First Item config.jei.category.categoryUidOrder=Category UID Order config.jei.category.categoryUidOrder.comment=Determines the display order of recipe categories in HEI. Categories listed earlier appear first in the HEI interface. diff --git a/src/main/resources/assets/jei/lang/es_es.lang b/src/main/resources/assets/jei/lang/es_es.lang index f5e411e00..f69752efc 100644 --- a/src/main/resources/assets/jei/lang/es_es.lang +++ b/src/main/resources/assets/jei/lang/es_es.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Ocultar botón de marcador en la esquina inferior izquierda -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si se debe ocultar el botón de marcador en la esquina inferior izquierda \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si se debe ocultar el botón de marcador en la esquina inferior izquierda + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Clic para expandir +jei.tooltip.collapsed.expand.firstItem=Alt+Clic para primer objeto +jei.tooltip.collapsed.collapse=Alt+Clic para colapsar + +config.jei.collapsible=Grupos colapsables +config.jei.collapsible.comment=Opciones para colapsar grupos de ingredientes en una sola ranura en la lista de ingredientes. +config.jei.collapsible.collapsibleGroupsEnabled=Habilitar grupos colapsables +config.jei.collapsible.collapsibleGroupsEnabled.comment=Cuando está habilitado, los grupos de ingredientes relacionados (pociones, libros encantados, etc.) se colapsan en una sola ranura. Alt+Clic en un grupo para expandirlo o colapsarlo. +config.jei.collapsible.collapseOnClose=Colapsar al cerrar +config.jei.collapsible.collapseOnClose.comment=Colapsa los grupos actualmente abiertos cuando se cierra HEI. +config.jei.collapsible.collapsedClickAction=Acción de clic predeterminada +config.jei.collapsible.collapsedClickAction.comment=Intercambia la acción del clic simple, la otra opción se convierte en la acción de Alt+Clic. Abrir Grupo abre el grupo. Primer Objeto aplica la acción al primer objeto del grupo. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Abrir Grupo +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Primer Objeto + +# Group Management GUI +jei.gui.collapsible.title=Gestionar grupos +jei.gui.collapsible.back=Atrás +jei.gui.collapsible.newGroup=Nuevo grupo +jei.gui.collapsible.title.tooltip=Activar/desactivar grupos integrados y crear grupos definidos por el usuario. +jei.gui.collapsible.enabled=Activado +jei.gui.collapsible.disabled=Desactivado +jei.gui.collapsible.customGroup=Personalizado +jei.gui.collapsible.defaultGroup=Predeterminado +jei.gui.collapsible.itemCount=%d objetos +jei.gui.collapsible.editor.title=Objetos seleccionados +jei.gui.collapsible.editor.name=Nombre +jei.gui.collapsible.editor.save=Guardar +jei.gui.collapsible.editor.selected=%d seleccionados diff --git a/src/main/resources/assets/jei/lang/fi_fi.lang b/src/main/resources/assets/jei/lang/fi_fi.lang index 8ee1f75a8..36b96e43b 100644 --- a/src/main/resources/assets/jei/lang/fi_fi.lang +++ b/src/main/resources/assets/jei/lang/fi_fi.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Piilota vasemman alan kulman kirjanmerkkipainike -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Piilotetaanko vasemman alan kulman kirjanmerkkipainike \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Piilotetaanko vasemman alan kulman kirjanmerkkipainike + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Klikkaa laajentaaksesi +jei.tooltip.collapsed.expand.firstItem=Alt+Klikkaa ensimmäiselle esineelle +jei.tooltip.collapsed.collapse=Alt+Klikkaa tiivistääksesi + +config.jei.collapsible=Tiivistetyt ryhmät +config.jei.collapsible.comment=Asetukset ainesosaryhmien tiivistämiseen yhdeksi paikaksi ainesosaluettelossa. +config.jei.collapsible.collapsibleGroupsEnabled=Ota tiivistetyt ryhmät käyttöön +config.jei.collapsible.collapsibleGroupsEnabled.comment=Kun käytössä, toisiinsa liittyvien ainesosien ryhmät (juomat, lumotut kirjat jne.) tiivistetään yhteen paikkaan. Alt+Klikkaa ryhmää laajentaaksesi tai tiivistääksesi sen. +config.jei.collapsible.collapseOnClose=Tiivistä suljettaessa +config.jei.collapsible.collapseOnClose.comment=Tiivistetään avoinna olevat ryhmät kun HEI suljetaan. +config.jei.collapsible.collapsedClickAction=Oletusklikkitoiminto +config.jei.collapsible.collapsedClickAction.comment=Vaihtaa yksittäisklikkauksen toiminnon, toinen vaihtoehto tulee Alt+Klikkaus-toiminnoksi. Avaa ryhmä avaa ryhmän. Ensimmäinen esine soveltaa toiminnon ryhmän ensimmäiseen esineeseen. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Avaa ryhmä +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Ensimmäinen esine + +# Group Management GUI +jei.gui.collapsible.title=Hallitse ryhmiä +jei.gui.collapsible.back=Takaisin +jei.gui.collapsible.newGroup=Uusi ryhmä +jei.gui.collapsible.title.tooltip=Sisäänrakennettujen ryhmien käyttöönotto/poistaminen käytöstä ja käyttäjän määrittämien ryhmien luominen. +jei.gui.collapsible.enabled=Käytössä +jei.gui.collapsible.disabled=Ei käytössä +jei.gui.collapsible.customGroup=Mukautettu +jei.gui.collapsible.defaultGroup=Oletus +jei.gui.collapsible.itemCount=%d esinettä +jei.gui.collapsible.editor.title=Valitut esineet +jei.gui.collapsible.editor.name=Nimi +jei.gui.collapsible.editor.save=Tallenna +jei.gui.collapsible.editor.selected=%d valittu diff --git a/src/main/resources/assets/jei/lang/fr_fr.lang b/src/main/resources/assets/jei/lang/fr_fr.lang index 36a931f05..61788051e 100644 --- a/src/main/resources/assets/jei/lang/fr_fr.lang +++ b/src/main/resources/assets/jei/lang/fr_fr.lang @@ -128,4 +128,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Masquer le bouton de marque-page en bas à gauche -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si le bouton de marque-page en bas à gauche doit être masqué \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si le bouton de marque-page en bas à gauche doit être masqué + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Clic pour développer +jei.tooltip.collapsed.expand.firstItem=Alt+Clic pour le premier objet +jei.tooltip.collapsed.collapse=Alt+Clic pour réduire + +config.jei.collapsible=Groupes réductibles +config.jei.collapsible.comment=Options pour réduire les groupes d'ingrédients en un seul emplacement dans la liste des ingrédients. +config.jei.collapsible.collapsibleGroupsEnabled=Activer les groupes réductibles +config.jei.collapsible.collapsibleGroupsEnabled.comment=Lorsqu'activé, les groupes d'ingrédients liés (potions, livres enchantés, etc.) sont réduits en un seul emplacement. Alt+Clic sur un groupe pour le développer ou le réduire. +config.jei.collapsible.collapseOnClose=Réduire à la fermeture +config.jei.collapsible.collapseOnClose.comment=Réduit les groupes actuellement ouverts lorsque HEI est fermé. +config.jei.collapsible.collapsedClickAction=Action de clic par défaut +config.jei.collapsible.collapsedClickAction.comment=Échange l'action du clic simple, l'autre option devient l'action Alt+Clic. Ouvrir le groupe ouvre le groupe. Premier objet applique l'action au premier objet du groupe. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Ouvrir le groupe +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Premier objet + +# Group Management GUI +jei.gui.collapsible.title=Gérer les groupes +jei.gui.collapsible.back=Retour +jei.gui.collapsible.newGroup=Nouveau groupe +jei.gui.collapsible.title.tooltip=Activer/désactiver les groupes intégrés et créer des groupes définis par l'utilisateur. +jei.gui.collapsible.enabled=Activé +jei.gui.collapsible.disabled=Désactivé +jei.gui.collapsible.customGroup=Personnalisé +jei.gui.collapsible.defaultGroup=Par défaut +jei.gui.collapsible.itemCount=%d objets +jei.gui.collapsible.editor.title=Objets sélectionnés +jei.gui.collapsible.editor.name=Nom +jei.gui.collapsible.editor.save=Enregistrer +jei.gui.collapsible.editor.selected=%d sélectionnés diff --git a/src/main/resources/assets/jei/lang/he_il.lang b/src/main/resources/assets/jei/lang/he_il.lang index 60b27c0fa..a5c244e6c 100644 --- a/src/main/resources/assets/jei/lang/he_il.lang +++ b/src/main/resources/assets/jei/lang/he_il.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=הסתר כפתור סימנייה בפינה השמאלית התחתונה -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=האם להסתיר את כפתור הסימנייה בפינה השמאלית התחתונה \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=האם להסתיר את כפתור הסימנייה בפינה השמאלית התחתונה + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+לחיצה כדי להרחיב +jei.tooltip.collapsed.expand.firstItem=Alt+לחיצה לפריט הראשון +jei.tooltip.collapsed.collapse=Alt+לחיצה כדי לכווץ + +config.jei.collapsible=קבוצות מתכווצות +config.jei.collapsible.comment=אפשרויות לכיווץ קבוצות רכיבים לתא יחיד ברשימת הרכיבים. +config.jei.collapsible.collapsibleGroupsEnabled=הפעל קבוצות מתכווצות +config.jei.collapsible.collapsibleGroupsEnabled.comment=כאשר מופעל, קבוצות של רכיבים קשורים (שיקויים, ספרים מכושפים וכו') מכווצות לתא יחיד. Alt+לחיצה על קבוצה כדי להרחיב או לכווץ אותה. +config.jei.collapsible.collapseOnClose=כיווץ בסגירה +config.jei.collapsible.collapseOnClose.comment=כיווץ קבוצות פתוחות כאשר HEI נסגר. +config.jei.collapsible.collapsedClickAction=פעולת לחיצה ברירת מחדל +config.jei.collapsible.collapsedClickAction.comment=מחליף את פעולת הלחיצה הבודדת, האפשרות השנייה הופכת לפעולת Alt+לחיצה. פתח קבוצה פותח את הקבוצה. פריט ראשון מחיל את הפעולה על הפריט הראשון בקבוצה. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=פתח קבוצה +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=פריט ראשון + +# Group Management GUI +jei.gui.collapsible.title=ניהול קבוצות +jei.gui.collapsible.back=חזרה +jei.gui.collapsible.newGroup=קבוצה חדשה +jei.gui.collapsible.title.tooltip=הפעלה/השבתה של קבוצות מובנות ויצירת קבוצות מותאמות אישית. +jei.gui.collapsible.enabled=מופעל +jei.gui.collapsible.disabled=מושבת +jei.gui.collapsible.customGroup=מותאם אישית +jei.gui.collapsible.defaultGroup=ברירת מחדל +jei.gui.collapsible.itemCount=%d פריטים +jei.gui.collapsible.editor.title=פריטים נבחרים +jei.gui.collapsible.editor.name=שם +jei.gui.collapsible.editor.save=שמירה +jei.gui.collapsible.editor.selected=%d נבחרו \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/it_it.lang b/src/main/resources/assets/jei/lang/it_it.lang index 24fd4fc1f..f0428d3ea 100644 --- a/src/main/resources/assets/jei/lang/it_it.lang +++ b/src/main/resources/assets/jei/lang/it_it.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Nascondi pulsante segnalibro nell'angolo in basso a sinistra -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se nascondere il pulsante segnalibro nell'angolo in basso a sinistra \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se nascondere il pulsante segnalibro nell'angolo in basso a sinistra + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Clic per espandere +jei.tooltip.collapsed.expand.firstItem=Alt+Clic per il primo oggetto +jei.tooltip.collapsed.collapse=Alt+Clic per comprimere + +config.jei.collapsible=Gruppi comprimibili +config.jei.collapsible.comment=Opzioni per comprimere gruppi di ingredienti in un singolo slot nell'elenco degli ingredienti. +config.jei.collapsible.collapsibleGroupsEnabled=Abilita gruppi comprimibili +config.jei.collapsible.collapsibleGroupsEnabled.comment=Quando abilitato, i gruppi di ingredienti correlati (pozioni, libri incantati, ecc.) vengono compressi in un singolo slot. Alt+Clic su un gruppo per espanderlo o comprimerlo. +config.jei.collapsible.collapseOnClose=Comprimi alla chiusura +config.jei.collapsible.collapseOnClose.comment=Comprime i gruppi attualmente aperti quando HEI viene chiuso. +config.jei.collapsible.collapsedClickAction=Azione clic predefinita +config.jei.collapsible.collapsedClickAction.comment=Scambia l'azione del clic singolo, l'altra opzione diventa l'azione Alt+Clic. Apri Gruppo apre il gruppo. Primo Oggetto applica l'azione al primo oggetto del gruppo. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Apri Gruppo +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Primo Oggetto + +# Group Management GUI +jei.gui.collapsible.title=Gestisci gruppi +jei.gui.collapsible.back=Indietro +jei.gui.collapsible.newGroup=Nuovo gruppo +jei.gui.collapsible.title.tooltip=Abilita/disabilita i gruppi integrati e crea gruppi definiti dall'utente. +jei.gui.collapsible.enabled=Abilitato +jei.gui.collapsible.disabled=Disabilitato +jei.gui.collapsible.customGroup=Personalizzato +jei.gui.collapsible.defaultGroup=Predefinito +jei.gui.collapsible.itemCount=%d oggetti +jei.gui.collapsible.editor.title=Oggetti selezionati +jei.gui.collapsible.editor.name=Nome +jei.gui.collapsible.editor.save=Salva +jei.gui.collapsible.editor.selected=%d selezionati \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/ja_jp.lang b/src/main/resources/assets/jei/lang/ja_jp.lang index a5ffd9b0e..bb082fa97 100644 --- a/src/main/resources/assets/jei/lang/ja_jp.lang +++ b/src/main/resources/assets/jei/lang/ja_jp.lang @@ -128,4 +128,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=左下隅のブックマークボタンを非表示にする -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=左下隅のブックマークボタンを非表示にするかどうか \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=左下隅のブックマークボタンを非表示にするかどうか + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+クリックで展開 +jei.tooltip.collapsed.expand.firstItem=Alt+クリックで最初のアイテム +jei.tooltip.collapsed.collapse=Alt+クリックで折りたたむ + +config.jei.collapsible=折りたたみグループ +config.jei.collapsible.comment=アイテムリスト内でアイテムグループをひとつのスロットに折りたたむオプション。 +config.jei.collapsible.collapsibleGroupsEnabled=折りたたみグループを有効にする +config.jei.collapsible.collapsibleGroupsEnabled.comment=有効にすると、関連するアイテムのグループ(ポーション、エンチャント本など)がひとつのスロットに折りたたまれます。Alt+クリックでグループを展開または折りたたみます。 +config.jei.collapsible.collapseOnClose=閉じる時に折りたたむ +config.jei.collapsible.collapseOnClose.comment=HEIを閉じた時に現在開いているグループを折りたたみます。 +config.jei.collapsible.collapsedClickAction=デフォルトのクリック動作 +config.jei.collapsible.collapsedClickAction.comment=シングルクリックの動作を切り替えます。もう一方のオプションがAlt+クリックの動作になります。グループを開くはグループを開きます。最初のアイテムはグループの最初のアイテムにアクションを適用します。 +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=グループを開く +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=最初のアイテム + +# Group Management GUI +jei.gui.collapsible.title=グループ管理 +jei.gui.collapsible.back=戻る +jei.gui.collapsible.newGroup=新しいグループ +jei.gui.collapsible.title.tooltip=組み込みグループを有効/無効にし、ユーザー定義グループを作成します。 +jei.gui.collapsible.enabled=有効 +jei.gui.collapsible.disabled=無効 +jei.gui.collapsible.customGroup=カスタム +jei.gui.collapsible.defaultGroup=デフォルト +jei.gui.collapsible.itemCount=%d アイテム +jei.gui.collapsible.editor.title=選択されたアイテム +jei.gui.collapsible.editor.name=名前 +jei.gui.collapsible.editor.save=保存 +jei.gui.collapsible.editor.selected=%d 選択中 diff --git a/src/main/resources/assets/jei/lang/ko_kr.lang b/src/main/resources/assets/jei/lang/ko_kr.lang index f58094240..dc0711cfe 100644 --- a/src/main/resources/assets/jei/lang/ko_kr.lang +++ b/src/main/resources/assets/jei/lang/ko_kr.lang @@ -165,4 +165,35 @@ config.jei.misc.tooltipShowRecipeBy.comment=툴팁에 레시피가 언제 변경 config.jei.interface.defaultFluidContainerItem.comment=메뉴에서 유체 재료를 클릭할 시 표시할 기본 유체 보관 아이템을 지정합니다, (형식: 아이템 이름@메타데이터) config.jei.misc.hideBottomLeftCornerBookmarkButton=왼쪽 하단 모서리 북마크 버튼 숨기기 -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=왼쪽 하단 모서리 북마크 버튼을 숨길지 여부 \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=왼쪽 하단 모서리 북마크 버튼을 숨길지 여부 + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+클릭하여 펼치기 +jei.tooltip.collapsed.expand.firstItem=Alt+클릭하여 첫 번째 아이템 +jei.tooltip.collapsed.collapse=Alt+클릭하여 접기 + +config.jei.collapsible=접을 수 있는 그룹 +config.jei.collapsible.comment=재료 목록에서 재료 그룹을 하나의 슬롯으로 접는 옵션입니다. +config.jei.collapsible.collapsibleGroupsEnabled=접을 수 있는 그룹 활성화 +config.jei.collapsible.collapsibleGroupsEnabled.comment=활성화하면 관련 재료 그룹(물약, 마법이 부여된 책 등)이 하나의 슬롯으로 접힙니다. Alt+클릭으로 그룹을 펼치거나 접을 수 있습니다. +config.jei.collapsible.collapseOnClose=닫을 때 접기 +config.jei.collapsible.collapseOnClose.comment=HEI를 닫을 때 현재 열려 있는 그룹을 접습니다. +config.jei.collapsible.collapsedClickAction=기본 클릭 동작 +config.jei.collapsible.collapsedClickAction.comment=단일 클릭 동작을 전환합니다. 다른 옵션이 Alt+클릭 동작이 됩니다. 그룹 열기는 그룹을 엽니다. 첫 번째 아이템은 그룹의 첫 번째 아이템에 동작을 적용합니다. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=그룹 열기 +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=첫 번째 아이템 + +# Group Management GUI +jei.gui.collapsible.title=그룹 관리 +jei.gui.collapsible.back=뒤로 +jei.gui.collapsible.newGroup=새 그룹 +jei.gui.collapsible.title.tooltip=기본 제공 그룹을 활성화/비활성화하고 사용자 정의 그룹을 만듭니다. +jei.gui.collapsible.enabled=활성화됨 +jei.gui.collapsible.disabled=비활성화됨 +jei.gui.collapsible.customGroup=사용자 정의 +jei.gui.collapsible.defaultGroup=기본값 +jei.gui.collapsible.itemCount=%d 아이템 +jei.gui.collapsible.editor.title=선택된 아이템 +jei.gui.collapsible.editor.name=이름 +jei.gui.collapsible.editor.save=저장 +jei.gui.collapsible.editor.selected=%d 선택됨 diff --git a/src/main/resources/assets/jei/lang/lt_lt.lang b/src/main/resources/assets/jei/lang/lt_lt.lang index e1cb177c4..69d0de74f 100644 --- a/src/main/resources/assets/jei/lang/lt_lt.lang +++ b/src/main/resources/assets/jei/lang/lt_lt.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Paslėpti žymės mygtuką apatiniame kairiajame kampe -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ar paslėpti žymės mygtuką apatiniame kairiajame kampe \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ar paslėpti žymės mygtuką apatiniame kairiajame kampe + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+spustelėkite, kad išskleistumėte +jei.tooltip.collapsed.expand.firstItem=Alt+spustelėkite pirmam daiktui +jei.tooltip.collapsed.collapse=Alt+spustelėkite, kad sutrauktumėte + +config.jei.collapsible=Sutraukiamos grupės +config.jei.collapsible.comment=Parinktys ingredientų grupių sutraukimui į vieną vietą ingredientų sąraše. +config.jei.collapsible.collapsibleGroupsEnabled=Įjungti sutraukiamas grupes +config.jei.collapsible.collapsibleGroupsEnabled.comment=Kai įjungta, susijusių ingredientų grupės (eliksyrai, užkeiktos knygos ir kt.) sutraukiamos į vieną vietą. Alt+spustelėkite grupę, kad ją išskleistumėte ar sutrauktumėte. +config.jei.collapsible.collapseOnClose=Sutraukti uždarant +config.jei.collapsible.collapseOnClose.comment=Sutraukti šiuo metu atidarytas grupes kai HEI uždaromas. +config.jei.collapsible.collapsedClickAction=Numatytasis paspaudimo veiksmas +config.jei.collapsible.collapsedClickAction.comment=Pakeičia vieno paspaudimo veiksmą, kita parinktis tampa Alt+spustelėjimo veiksmu. Atidaryti grupę atidaro grupę. Pirmas daiktas taiko veiksmą pirmam grupės daiktui. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Atidaryti grupę +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Pirmas daiktas + +# Group Management GUI +jei.gui.collapsible.title=Valdyti grupes +jei.gui.collapsible.back=Atgal +jei.gui.collapsible.newGroup=Nauja grupė +jei.gui.collapsible.title.tooltip=Įgąlinti/išjungti įtaisytąsias grupes ir kurti naudotojo apibrėžtas grupes. +jei.gui.collapsible.enabled=Įgąlinta +jei.gui.collapsible.disabled=Išjungta +jei.gui.collapsible.customGroup=Pasirinktiniű +jei.gui.collapsible.defaultGroup=Numatytoji +jei.gui.collapsible.itemCount=%d daiktų +jei.gui.collapsible.editor.title=Pasirinkti daiktai +jei.gui.collapsible.editor.name=Pavadinimas +jei.gui.collapsible.editor.save=Išsaugoti +jei.gui.collapsible.editor.selected=%d pasirinkta diff --git a/src/main/resources/assets/jei/lang/nb_no.lang b/src/main/resources/assets/jei/lang/nb_no.lang index 17aa90692..9f2dfffb8 100644 --- a/src/main/resources/assets/jei/lang/nb_no.lang +++ b/src/main/resources/assets/jei/lang/nb_no.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Skjul bokmerke-knapp i nedre venstre hjørne -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmerke-knappen i nedre venstre hjørne skal skjules \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmerke-knappen i nedre venstre hjørne skal skjules + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Klikk for å utvide +jei.tooltip.collapsed.expand.firstItem=Alt+Klikk for første gjenstand +jei.tooltip.collapsed.collapse=Alt+Klikk for å skjule + +config.jei.collapsible=Sammenleggbare grupper +config.jei.collapsible.comment=Alternativer for å slå sammen grupper av ingredienser til én plass i ingredienslisten. +config.jei.collapsible.collapsibleGroupsEnabled=Aktiver sammenleggbare grupper +config.jei.collapsible.collapsibleGroupsEnabled.comment=Når aktivert, blir grupper av relaterte ingredienser (drikkevarer, forheksede bøker osv.) slått sammen til én plass. Alt+Klikk på en gruppe for å utvide eller skjule den. +config.jei.collapsible.collapseOnClose=Legg sammen ved lukking +config.jei.collapsible.collapseOnClose.comment=Legger sammen åpne grupper når HEI lukkes. +config.jei.collapsible.collapsedClickAction=Standard klikkhandling +config.jei.collapsible.collapsedClickAction.comment=Bytter enkeltklikkhandlingen, det andre alternativet blir Alt+Klikk-handlingen. Åpne gruppe åpner gruppen. Første gjenstand bruker handlingen på den første gjenstanden i gruppen. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Åpne gruppe +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Første gjenstand + +# Group Management GUI +jei.gui.collapsible.title=Administrer grupper +jei.gui.collapsible.back=Tilbake +jei.gui.collapsible.newGroup=Ny gruppe +jei.gui.collapsible.title.tooltip=Aktiver/deaktiver innebygde grupper og opprett brukerdefinerte grupper. +jei.gui.collapsible.enabled=Aktivert +jei.gui.collapsible.disabled=Deaktivert +jei.gui.collapsible.customGroup=Egendefinert +jei.gui.collapsible.defaultGroup=Standard +jei.gui.collapsible.itemCount=%d gjenstander +jei.gui.collapsible.editor.title=Valgte gjenstander +jei.gui.collapsible.editor.name=Navn +jei.gui.collapsible.editor.save=Lagre +jei.gui.collapsible.editor.selected=%d valgt diff --git a/src/main/resources/assets/jei/lang/pl_pl.lang b/src/main/resources/assets/jei/lang/pl_pl.lang index 190cfb940..591f99bea 100644 --- a/src/main/resources/assets/jei/lang/pl_pl.lang +++ b/src/main/resources/assets/jei/lang/pl_pl.lang @@ -131,4 +131,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Ukryj przycisk zakładki w lewym dolnym rogu -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Czy ukryć przycisk zakładki w lewym dolnym rogu \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Czy ukryć przycisk zakładki w lewym dolnym rogu + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Klik aby rozwinąć +jei.tooltip.collapsed.expand.firstItem=Alt+Klik dla pierwszego przedmiotu +jei.tooltip.collapsed.collapse=Alt+Klik aby zwinąć + +config.jei.collapsible=Zwijane grupy +config.jei.collapsible.comment=Opcje zwijania grup składników do jednego slotu na liście składników. +config.jei.collapsible.collapsibleGroupsEnabled=Włącz zwijane grupy +config.jei.collapsible.collapsibleGroupsEnabled.comment=Po włączeniu grupy powiązanych składników (mikstury, zaklęte księgi itp.) są zwijane do jednego slotu. Alt+Klik na grupę, aby ją rozwinąć lub zwinąć. +config.jei.collapsible.collapseOnClose=Zwiń przy zamknięciu +config.jei.collapsible.collapseOnClose.comment=Zwija aktualnie otwarte grupy po zamknięciu HEI. +config.jei.collapsible.collapsedClickAction=Domyślna akcja kliknięcia +config.jei.collapsible.collapsedClickAction.comment=Zamienia akcję pojedynczego kliknięcia, druga opcja staje się akcją Alt+Klik. Otwórz grupę otwiera grupę. Pierwszy przedmiot stosuje akcję do pierwszego przedmiotu w grupie. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Otwórz grupę +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Pierwszy przedmiot + +# Group Management GUI +jei.gui.collapsible.title=Zarządzaj grupami +jei.gui.collapsible.back=Wstecz +jei.gui.collapsible.newGroup=Nowa grupa +jei.gui.collapsible.title.tooltip=Włącz/wyłącz wbudowane grupy i twórz grupy zdefiniowane przez użytkownika. +jei.gui.collapsible.enabled=Włączone +jei.gui.collapsible.disabled=Wyłączone +jei.gui.collapsible.customGroup=Niestandardowe +jei.gui.collapsible.defaultGroup=Domyślne +jei.gui.collapsible.itemCount=%d przedmiotów +jei.gui.collapsible.editor.title=Wybrane przedmioty +jei.gui.collapsible.editor.name=Nazwa +jei.gui.collapsible.editor.save=Zapisz +jei.gui.collapsible.editor.selected=%d wybranych diff --git a/src/main/resources/assets/jei/lang/pt_br.lang b/src/main/resources/assets/jei/lang/pt_br.lang index b376dd2a9..7c6a8dda7 100644 --- a/src/main/resources/assets/jei/lang/pt_br.lang +++ b/src/main/resources/assets/jei/lang/pt_br.lang @@ -128,4 +128,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Ocultar botão de favorito no canto inferior esquerdo -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se o botão de favorito no canto inferior esquerdo deve ser ocultado \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se o botão de favorito no canto inferior esquerdo deve ser ocultado + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Clique para expandir +jei.tooltip.collapsed.expand.firstItem=Alt+Clique para o primeiro item +jei.tooltip.collapsed.collapse=Alt+Clique para recolher + +config.jei.collapsible=Grupos recolhíveis +config.jei.collapsible.comment=Opções para recolher grupos de ingredientes em um único slot na lista de ingredientes. +config.jei.collapsible.collapsibleGroupsEnabled=Habilitar grupos recolhíveis +config.jei.collapsible.collapsibleGroupsEnabled.comment=Quando habilitado, grupos de ingredientes relacionados (poções, livros encantados, etc.) são recolhidos em um único slot. Alt+Clique em um grupo para expandir ou recolher. +config.jei.collapsible.collapseOnClose=Recolher ao fechar +config.jei.collapsible.collapseOnClose.comment=Recolhe os grupos atualmente abertos quando o HEI é fechado. +config.jei.collapsible.collapsedClickAction=Ação de clique padrão +config.jei.collapsible.collapsedClickAction.comment=Troca a ação do clique simples, a outra opção se torna a ação Alt+Clique. Abrir Grupo abre o grupo. Primeiro Item aplica a ação ao primeiro item do grupo. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Abrir Grupo +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Primeiro Item + +# Group Management GUI +jei.gui.collapsible.title=Gerenciar grupos +jei.gui.collapsible.back=Voltar +jei.gui.collapsible.newGroup=Novo grupo +jei.gui.collapsible.title.tooltip=Ativar/desativar grupos integrados e criar grupos definidos pelo usuário. +jei.gui.collapsible.enabled=Ativado +jei.gui.collapsible.disabled=Desativado +jei.gui.collapsible.customGroup=Personalizado +jei.gui.collapsible.defaultGroup=Padrão +jei.gui.collapsible.itemCount=%d itens +jei.gui.collapsible.editor.title=Itens selecionados +jei.gui.collapsible.editor.name=Nome +jei.gui.collapsible.editor.save=Salvar +jei.gui.collapsible.editor.selected=%d selecionados diff --git a/src/main/resources/assets/jei/lang/ru_ru.lang b/src/main/resources/assets/jei/lang/ru_ru.lang index cc59257e3..ce24d152b 100644 --- a/src/main/resources/assets/jei/lang/ru_ru.lang +++ b/src/main/resources/assets/jei/lang/ru_ru.lang @@ -163,4 +163,35 @@ config.jei.interface.defaultFluidContainerItem=Контейнер жидкост config.jei.interface.defaultFluidContainerItem.comment=Предмет по умолчанию, используемый для заполнения жидкостей при нажатии на жидкий ингредиент в меню (формат: item_name@meta) config.jei.misc.hideBottomLeftCornerBookmarkButton=Скрыть кнопку закладки в левом нижнем углу -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Скрывать ли кнопку закладки в левом нижнем углу \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Скрывать ли кнопку закладки в левом нижнем углу + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Клик для раскрытия +jei.tooltip.collapsed.expand.firstItem=Alt+Клик для первого предмета +jei.tooltip.collapsed.collapse=Alt+Клик для сворачивания + +config.jei.collapsible=Сворачиваемые группы +config.jei.collapsible.comment=Настройки сворачивания групп ингредиентов в один слот в списке ингредиентов. +config.jei.collapsible.collapsibleGroupsEnabled=Включить сворачиваемые группы +config.jei.collapsible.collapsibleGroupsEnabled.comment=При включении группы связанных ингредиентов (зелья, зачарованные книги и т.д.) сворачиваются в один слот. Alt+Клик по группе для раскрытия или сворачивания. +config.jei.collapsible.collapseOnClose=Сворачивать при закрытии +config.jei.collapsible.collapseOnClose.comment=Сворачивать открытые группы при закрытии HEI. +config.jei.collapsible.collapsedClickAction=Действие клика по умолчанию +config.jei.collapsible.collapsedClickAction.comment=Меняет действие одиночного клика, другой вариант становится действием Alt+Клик. Открыть группу открывает группу. Первый предмет применяет действие к первому предмету в группе. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Открыть группу +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Первый предмет + +# Group Management GUI +jei.gui.collapsible.title=Управление группами +jei.gui.collapsible.back=Назад +jei.gui.collapsible.newGroup=Новая группа +jei.gui.collapsible.title.tooltip=Включение/отключение встроенных групп и создание пользовательских групп. +jei.gui.collapsible.enabled=Включено +jei.gui.collapsible.disabled=Отключено +jei.gui.collapsible.customGroup=Пользовательская +jei.gui.collapsible.defaultGroup=По умолчанию +jei.gui.collapsible.itemCount=%d предметов +jei.gui.collapsible.editor.title=Выбранные предметы +jei.gui.collapsible.editor.name=Название +jei.gui.collapsible.editor.save=Сохранить +jei.gui.collapsible.editor.selected=%d выбрано diff --git a/src/main/resources/assets/jei/lang/sv_se.lang b/src/main/resources/assets/jei/lang/sv_se.lang index ff836ec52..11b8edeb1 100644 --- a/src/main/resources/assets/jei/lang/sv_se.lang +++ b/src/main/resources/assets/jei/lang/sv_se.lang @@ -131,4 +131,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Dölj bokmärkesknapp i nedre vänstra hörnet -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmärkesknappen i nedre vänstra hörnet ska döljas \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmärkesknappen i nedre vänstra hörnet ska döljas + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Klicka för att expandera +jei.tooltip.collapsed.expand.firstItem=Alt+Klicka för första föremålet +jei.tooltip.collapsed.collapse=Alt+Klicka för att fälla ihop + +config.jei.collapsible=Ihopfällbara grupper +config.jei.collapsible.comment=Alternativ för att fälla ihop grupper av ingredienser till en enda plats i ingredienslistan. +config.jei.collapsible.collapsibleGroupsEnabled=Aktivera ihopfällbara grupper +config.jei.collapsible.collapsibleGroupsEnabled.comment=När aktiverat fälls grupper av relaterade ingredienser (bryggdrycker, förtrollade böcker osv.) ihop till en enda plats. Alt+Klicka på en grupp för att expandera eller fälla ihop den. +config.jei.collapsible.collapseOnClose=Fäll ihop vid stängning +config.jei.collapsible.collapseOnClose.comment=Fäller ihop öppna grupper när HEI stängs. +config.jei.collapsible.collapsedClickAction=Standardklickåtgärd +config.jei.collapsible.collapsedClickAction.comment=Byter enkelklicksåtgärden, det andra alternativet blir Alt+Klick-åtgärden. Öppna grupp öppnar gruppen. Första föremålet tillämpar åtgärden på gruppens första föremål. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Öppna grupp +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Första föremålet + +# Group Management GUI +jei.gui.collapsible.title=Hantera grupper +jei.gui.collapsible.back=Tillbaka +jei.gui.collapsible.newGroup=Ny grupp +jei.gui.collapsible.title.tooltip=Aktivera/inaktivera inbyggda grupper och skapa användardefinierade grupper. +jei.gui.collapsible.enabled=Aktiverad +jei.gui.collapsible.disabled=Inaktiverad +jei.gui.collapsible.customGroup=Anpassad +jei.gui.collapsible.defaultGroup=Standard +jei.gui.collapsible.itemCount=%d föremål +jei.gui.collapsible.editor.title=Valda föremål +jei.gui.collapsible.editor.name=Namn +jei.gui.collapsible.editor.save=Spara +jei.gui.collapsible.editor.selected=%d valda diff --git a/src/main/resources/assets/jei/lang/tr_tr.lang b/src/main/resources/assets/jei/lang/tr_tr.lang index d3d1692bd..7b2a4ccde 100644 --- a/src/main/resources/assets/jei/lang/tr_tr.lang +++ b/src/main/resources/assets/jei/lang/tr_tr.lang @@ -118,4 +118,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Sol alt köşedeki yer imi düğmesini gizle -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Sol alt köşedeki yer imi düğmesinin gizlenip gizlenmeyeceği \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Sol alt köşedeki yer imi düğmesinin gizlenip gizlenmeyeceği + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Tıkla genişletmek için +jei.tooltip.collapsed.expand.firstItem=Alt+Tıkla ilk öğe için +jei.tooltip.collapsed.collapse=Alt+Tıkla daraltmak için + +config.jei.collapsible=Daraltılabilir Gruplar +config.jei.collapsible.comment=Malzeme listesinde malzeme gruplarını tek bir yuvaya daraltma seçenekleri. +config.jei.collapsible.collapsibleGroupsEnabled=Daraltılabilir Grupları Etkinleştir +config.jei.collapsible.collapsibleGroupsEnabled.comment=Etkinleştirildiğinde, ilgili malzeme grupları (iksirler, büyülü kitaplar vb.) tek bir yuvaya daraltılır. Alt+Tıkla ile bir grubu genişletin veya daraltın. +config.jei.collapsible.collapseOnClose=Kapatırken Daralt +config.jei.collapsible.collapseOnClose.comment=HEI kapatıldığında açık olan grupları daraltır. +config.jei.collapsible.collapsedClickAction=Varsayılan Tıklama Eylemi +config.jei.collapsible.collapsedClickAction.comment=Tek tıklama eylemini değiştirir, diğer seçenek Alt+Tıklama eylemi olur. Grubu Aç grubu açar. İlk Öğe eylemi gruptaki ilk öğeye uygular. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Grubu Aç +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=İlk Öğe + +# Group Management GUI +jei.gui.collapsible.title=Grupları Yönet +jei.gui.collapsible.back=Geri +jei.gui.collapsible.newGroup=Yeni Grup +jei.gui.collapsible.title.tooltip=Yerleşik grupları etkinleştirin/devre dışı bırakın ve kullanıcı tanımlı gruplar oluşturun. +jei.gui.collapsible.enabled=Etkin +jei.gui.collapsible.disabled=Devre dışı +jei.gui.collapsible.customGroup=Özel +jei.gui.collapsible.defaultGroup=Varsayılan +jei.gui.collapsible.itemCount=%d öğe +jei.gui.collapsible.editor.title=Seçili Öğeler +jei.gui.collapsible.editor.name=Ad +jei.gui.collapsible.editor.save=Kaydet +jei.gui.collapsible.editor.selected=%d seçildi diff --git a/src/main/resources/assets/jei/lang/uk_ua.lang b/src/main/resources/assets/jei/lang/uk_ua.lang index 14c79d16f..5e9d8e49e 100644 --- a/src/main/resources/assets/jei/lang/uk_ua.lang +++ b/src/main/resources/assets/jei/lang/uk_ua.lang @@ -120,4 +120,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=Приховати кнопку закладки в лівому нижньому куті -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Чи приховувати кнопку закладки в лівому нижньому куті \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Чи приховувати кнопку закладки в лівому нижньому куті + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+Клік для розгортання +jei.tooltip.collapsed.expand.firstItem=Alt+Клік для першого предмета +jei.tooltip.collapsed.collapse=Alt+Клік для згортання + +config.jei.collapsible=Згортувані групи +config.jei.collapsible.comment=Налаштування згортання груп інгредієнтів в один слот у списку інгредієнтів. +config.jei.collapsible.collapsibleGroupsEnabled=Увімкнути згортувані групи +config.jei.collapsible.collapsibleGroupsEnabled.comment=Коли увімкнено, групи пов'язаних інгредієнтів (зілля, зачаровані книги тощо) згортаються в один слот. Alt+Клік по групі для розгортання або згортання. +config.jei.collapsible.collapseOnClose=Згорнути при закритті +config.jei.collapsible.collapseOnClose.comment=Згортає відкриті групи при закритті HEI. +config.jei.collapsible.collapsedClickAction=Дія кліку за замовчуванням +config.jei.collapsible.collapsedClickAction.comment=Змінює дію одиничного кліку, інший варіант стає дією Alt+Клік. Відкрити групу відкриває групу. Перший предмет застосовує дію до першого предмета в групі. +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Відкрити групу +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Перший предмет + +# Group Management GUI +jei.gui.collapsible.title=Управління групами +jei.gui.collapsible.back=Назад +jei.gui.collapsible.newGroup=Нова група +jei.gui.collapsible.title.tooltip=Увімкнення/вимкнення вбудованих груп та створення користувацьких груп. +jei.gui.collapsible.enabled=Увімкнено +jei.gui.collapsible.disabled=Вимкнено +jei.gui.collapsible.customGroup=Користувацька +jei.gui.collapsible.defaultGroup=За замовчуванням +jei.gui.collapsible.itemCount=%d предметів +jei.gui.collapsible.editor.title=Вибрані предмети +jei.gui.collapsible.editor.name=Назва +jei.gui.collapsible.editor.save=Зберегти +jei.gui.collapsible.editor.selected=%d вибрано diff --git a/src/main/resources/assets/jei/lang/zh_cn.lang b/src/main/resources/assets/jei/lang/zh_cn.lang index eeaf67a9a..4a55558c5 100644 --- a/src/main/resources/assets/jei/lang/zh_cn.lang +++ b/src/main/resources/assets/jei/lang/zh_cn.lang @@ -160,4 +160,35 @@ config.jei.interface.defaultFluidContainerItem=默认流体容器 config.jei.interface.defaultFluidContainerItem.comment=当点击 HEI 界面中的流体时,用来填充流体的默认物品(格式: item_name@meta) config.jei.misc.hideBottomLeftCornerBookmarkButton=隐藏左下角书签按钮 -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隐藏左下角书签按钮 \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隐藏左下角书签按钮 + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+点击展开 +jei.tooltip.collapsed.expand.firstItem=Alt+点击查看第一个物品 +jei.tooltip.collapsed.collapse=Alt+点击收起 + +config.jei.collapsible=可折叠分组 +config.jei.collapsible.comment=将配料分组折叠为配料列表中的单个格子的相关选项。 +config.jei.collapsible.collapsibleGroupsEnabled=启用可折叠分组 +config.jei.collapsible.collapsibleGroupsEnabled.comment=启用后,相关配料的分组(药水、附魔书等)将折叠为单个格子。Alt+点击分组以展开或折叠。 +config.jei.collapsible.collapseOnClose=关闭时折叠 +config.jei.collapsible.collapseOnClose.comment=关闭HEI时折叠当前已展开的分组。 +config.jei.collapsible.collapsedClickAction=默认点击操作 +config.jei.collapsible.collapsedClickAction.comment=切换单击操作,另一个选项变为Alt+点击操作。打开分组会展开分组。第一个物品会对分组中的第一个物品执行操作。 +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=打开分组 +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=第一个物品 + +# Group Management GUI +jei.gui.collapsible.title=管理分组 +jei.gui.collapsible.back=返回 +jei.gui.collapsible.newGroup=新建分组 +jei.gui.collapsible.title.tooltip=启用/禁用内置分组并创建用户自定义分组。 +jei.gui.collapsible.enabled=已启用 +jei.gui.collapsible.disabled=已禁用 +jei.gui.collapsible.customGroup=自定义 +jei.gui.collapsible.defaultGroup=默认 +jei.gui.collapsible.itemCount=%d 个物品 +jei.gui.collapsible.editor.title=已选物品 +jei.gui.collapsible.editor.name=名称 +jei.gui.collapsible.editor.save=保存 +jei.gui.collapsible.editor.selected=%d 已选择 diff --git a/src/main/resources/assets/jei/lang/zh_tw.lang b/src/main/resources/assets/jei/lang/zh_tw.lang index b7a87f345..31cbe60c0 100644 --- a/src/main/resources/assets/jei/lang/zh_tw.lang +++ b/src/main/resources/assets/jei/lang/zh_tw.lang @@ -128,4 +128,35 @@ description.jei.wooden.door.2=Clicking on a door changes its state from open to description.jei.wooden.door.3=Wooden Doors can be opened/closed via Redstone Circuits. config.jei.misc.hideBottomLeftCornerBookmarkButton=隱藏左下角書籤按鈕 -config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隱藏左下角書籤按鈕 \ No newline at end of file +config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隱藏左下角書籤按鈕 + +# Collapsible Groups +jei.tooltip.collapsed.expand=Alt+點擊展開 +jei.tooltip.collapsed.expand.firstItem=Alt+點擊查看第一個物品 +jei.tooltip.collapsed.collapse=Alt+點擊收合 + +config.jei.collapsible=可折疊群組 +config.jei.collapsible.comment=將材料群組折疊為材料列表中的單個格子的相關選項。 +config.jei.collapsible.collapsibleGroupsEnabled=啟用可折疊群組 +config.jei.collapsible.collapsibleGroupsEnabled.comment=啟用後,相關材料的群組(藥水、附魔書等)將折疊為單個格子。Alt+點擊群組以展開或收合。 +config.jei.collapsible.collapseOnClose=關閉時收合 +config.jei.collapsible.collapseOnClose.comment=關閉HEI時收合目前已展開的群組。 +config.jei.collapsible.collapsedClickAction=預設點擊操作 +config.jei.collapsible.collapsedClickAction.comment=切換單擊操作,另一個選項變為Alt+點擊操作。開啟群組會展開群組。第一個物品會對群組中的第一個物品執行操作。 +config.jei.collapsible.collapsedClickAction.OPEN_GROUP=開啟群組 +config.jei.collapsible.collapsedClickAction.FIRST_ITEM=第一個物品 + +# Group Management GUI +jei.gui.collapsible.title=管理群組 +jei.gui.collapsible.back=返回 +jei.gui.collapsible.newGroup=新建群組 +jei.gui.collapsible.title.tooltip=啟用/停用內建群組並建立使用者自訂群組。 +jei.gui.collapsible.enabled=已啟用 +jei.gui.collapsible.disabled=已停用 +jei.gui.collapsible.customGroup=自訂 +jei.gui.collapsible.defaultGroup=預設 +jei.gui.collapsible.itemCount=%d 個物品 +jei.gui.collapsible.editor.title=已選物品 +jei.gui.collapsible.editor.name=名稱 +jei.gui.collapsible.editor.save=儲存 +jei.gui.collapsible.editor.selected=%d 已選擇 From cd1183c5ae8131b5f45a5edf6a5c269e4240c896 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 14 Mar 2026 01:22:29 -0400 Subject: [PATCH 21/48] Implement Alt+Click toggle HEI config menu Implements an Alt+Click toggle to expand/collapse all groups via the HEI config button. Conditional tooltip to inform user. Includes translated keys. --- .../mezz/jei/gui/overlay/ConfigButton.java | 19 ++++++++++++++++++- src/main/resources/assets/jei/lang/ar_sa.lang | 1 + src/main/resources/assets/jei/lang/bg_bg.lang | 1 + src/main/resources/assets/jei/lang/cs_cz.lang | 1 + src/main/resources/assets/jei/lang/de_de.lang | 1 + src/main/resources/assets/jei/lang/el_gr.lang | 1 + src/main/resources/assets/jei/lang/en_au.lang | 1 + src/main/resources/assets/jei/lang/en_us.lang | 1 + src/main/resources/assets/jei/lang/es_es.lang | 1 + src/main/resources/assets/jei/lang/fi_fi.lang | 2 +- src/main/resources/assets/jei/lang/fr_fr.lang | 1 + src/main/resources/assets/jei/lang/he_il.lang | 1 + src/main/resources/assets/jei/lang/it_it.lang | 1 + src/main/resources/assets/jei/lang/ja_jp.lang | 1 + src/main/resources/assets/jei/lang/ko_kr.lang | 1 + src/main/resources/assets/jei/lang/lt_lt.lang | 1 + src/main/resources/assets/jei/lang/nb_no.lang | 1 + src/main/resources/assets/jei/lang/pl_pl.lang | 1 + src/main/resources/assets/jei/lang/pt_br.lang | 1 + src/main/resources/assets/jei/lang/ru_ru.lang | 1 + src/main/resources/assets/jei/lang/sv_se.lang | 1 + src/main/resources/assets/jei/lang/tr_tr.lang | 1 + src/main/resources/assets/jei/lang/uk_ua.lang | 1 + src/main/resources/assets/jei/lang/zh_cn.lang | 1 + src/main/resources/assets/jei/lang/zh_tw.lang | 1 + 25 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java index 70aa2f245..09e89faeb 100644 --- a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java +++ b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java @@ -1,5 +1,6 @@ package mezz.jei.gui.overlay; +import java.util.Collection; import java.util.List; import net.minecraft.client.Minecraft; @@ -14,6 +15,8 @@ import mezz.jei.config.KeyBindings; import mezz.jei.gui.GuiHelper; import mezz.jei.gui.elements.GuiIconToggleButton; +import mezz.jei.ingredients.CollapsibleEntry; +import mezz.jei.ingredients.CollapsibleEntryRegistry; import mezz.jei.util.Translator; import org.lwjgl.input.Keyboard; @@ -33,6 +36,9 @@ private ConfigButton(IDrawable disabledIcon, IDrawable enabledIcon, IngredientLi @Override protected void getTooltips(List tooltip) { tooltip.add(Translator.translateToLocal("jei.tooltip.config")); + if (Config.isOverlayEnabled() && Config.isCollapsibleGroupsEnabled()) { + tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.config.expandCollapseAll")); + } if (!Config.isOverlayEnabled()) { tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.ingredient.list.disabled")); tooltip.add(TextFormatting.GOLD + Translator.translateToLocalFormatted("jei.tooltip.ingredient.list.disabled.how.to.fix", KeyBindings.toggleOverlay.getDisplayName())); @@ -59,7 +65,18 @@ protected boolean isIconToggledOn() { @Override protected boolean onMouseClicked(int mouseX, int mouseY) { if (Config.isOverlayEnabled()) { - if (Keyboard.getEventKeyState() && (Keyboard.getEventKey() == Keyboard.KEY_LCONTROL || Keyboard.getEventKey() == Keyboard.KEY_RCONTROL)) { + if (GuiScreen.isAltKeyDown() && Config.isCollapsibleGroupsEnabled() + && Internal.hasIngredientFilter()) { + CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + Collection entries = registry.getEntries(); + List customEntries = registry.getCustomEntries(); + boolean allExpanded = entries.stream().allMatch(CollapsibleEntry::isExpanded) + && customEntries.stream().allMatch(CollapsibleEntry::isExpanded); + boolean targetExpanded = !allExpanded; + entries.forEach(e -> e.setExpanded(targetExpanded)); + customEntries.forEach(e -> e.setExpanded(targetExpanded)); + Internal.getIngredientFilter().notifyCollapsedStateChanged(); + } else if (Keyboard.getEventKeyState() && (Keyboard.getEventKey() == Keyboard.KEY_LCONTROL || Keyboard.getEventKey() == Keyboard.KEY_RCONTROL)) { Config.toggleCheatItemsEnabled(); } else { Minecraft minecraft = Minecraft.getMinecraft(); diff --git a/src/main/resources/assets/jei/lang/ar_sa.lang b/src/main/resources/assets/jei/lang/ar_sa.lang index 7d40f1b11..189c6fe6e 100644 --- a/src/main/resources/assets/jei/lang/ar_sa.lang +++ b/src/main/resources/assets/jei/lang/ar_sa.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=ما إذا كان س jei.tooltip.collapsed.expand=Alt+انقر للتوسيع jei.tooltip.collapsed.expand.firstItem=Alt+انقر للعنصر الأول jei.tooltip.collapsed.collapse=Alt+انقر للطي +jei.tooltip.config.expandCollapseAll=Alt+النقر لتبديل المجموعات config.jei.collapsible=المجموعات القابلة للطي config.jei.collapsible.comment=خيارات لطي مجموعات المكونات في خانة واحدة في قائمة المكونات. diff --git a/src/main/resources/assets/jei/lang/bg_bg.lang b/src/main/resources/assets/jei/lang/bg_bg.lang index 9a65cf31f..f14361e7c 100644 --- a/src/main/resources/assets/jei/lang/bg_bg.lang +++ b/src/main/resources/assets/jei/lang/bg_bg.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Дали да се с jei.tooltip.collapsed.expand=Alt+Клик за разгъване jei.tooltip.collapsed.expand.firstItem=Alt+Клик за първия предмет jei.tooltip.collapsed.collapse=Alt+Клик за свиване +jei.tooltip.config.expandCollapseAll=Alt+Клик за превключване на групи config.jei.collapsible=Свиваеми групи config.jei.collapsible.comment=Опции за свиване на групи от съставки в един слот в списъка със съставки. diff --git a/src/main/resources/assets/jei/lang/cs_cz.lang b/src/main/resources/assets/jei/lang/cs_cz.lang index 0141c8c94..ea9c50a61 100644 --- a/src/main/resources/assets/jei/lang/cs_cz.lang +++ b/src/main/resources/assets/jei/lang/cs_cz.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Zda skrýt tlačítko jei.tooltip.collapsed.expand=Alt+Klik pro rozbalení jei.tooltip.collapsed.expand.firstItem=Alt+Klik pro první předmět jei.tooltip.collapsed.collapse=Alt+Klik pro sbalení +jei.tooltip.config.expandCollapseAll=Alt+Klik pro přepínat skupiny config.jei.collapsible=Sbalitelné skupiny config.jei.collapsible.comment=Možnosti pro sbalení skupin ingrediencí do jednoho slotu v seznamu ingrediencí. diff --git a/src/main/resources/assets/jei/lang/de_de.lang b/src/main/resources/assets/jei/lang/de_de.lang index 4ea8e9b10..375fe6162 100644 --- a/src/main/resources/assets/jei/lang/de_de.lang +++ b/src/main/resources/assets/jei/lang/de_de.lang @@ -157,6 +157,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ob der Lesezeichen-Bu jei.tooltip.collapsed.expand=Alt+Klick zum Aufklappen jei.tooltip.collapsed.expand.firstItem=Alt+Klick für erstes Element jei.tooltip.collapsed.collapse=Alt+Klick zum Zuklappen +jei.tooltip.config.expandCollapseAll=Alt+Klick zum Gruppen umschalten config.jei.collapsible=Einklappbare Gruppen config.jei.collapsible.comment=Optionen zum Einklappen von Zutatengruppen in einen einzelnen Slot in der Zutatenliste. diff --git a/src/main/resources/assets/jei/lang/el_gr.lang b/src/main/resources/assets/jei/lang/el_gr.lang index f2d701998..83f678f6b 100644 --- a/src/main/resources/assets/jei/lang/el_gr.lang +++ b/src/main/resources/assets/jei/lang/el_gr.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Αν θα αποκρ jei.tooltip.collapsed.expand=Alt+Κλικ για ανάπτυξη jei.tooltip.collapsed.expand.firstItem=Alt+Κλικ για πρώτο αντικείμενο jei.tooltip.collapsed.collapse=Alt+Κλικ για σύμπτυξη +jei.tooltip.config.expandCollapseAll=Alt+Κλικ Εναλλαγή Ομάδων config.jei.collapsible=Πτυσσόμενες Ομάδες config.jei.collapsible.comment=Επιλογές για σύμπτυξη ομάδων συστατικών σε μία θέση στη λίστα συστατικών. diff --git a/src/main/resources/assets/jei/lang/en_au.lang b/src/main/resources/assets/jei/lang/en_au.lang index 60c05eafa..f0c6c8820 100644 --- a/src/main/resources/assets/jei/lang/en_au.lang +++ b/src/main/resources/assets/jei/lang/en_au.lang @@ -125,6 +125,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Whether to hide the b jei.tooltip.collapsed.expand=Alt+Click to expand jei.tooltip.collapsed.expand.firstItem=Alt+Click for first item jei.tooltip.collapsed.collapse=Alt+Click to collapse +jei.tooltip.config.expandCollapseAll=Alt+Click Toggle Groups config.jei.collapsible=Collapsible Groups config.jei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index a0028c189..cf2781917 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -26,6 +26,7 @@ jei.tooltip.bookmarks.not.enough.space=There is not enough space to display book jei.tooltip.collapsed.expand=Alt+Click to Expand jei.tooltip.collapsed.expand.firstItem=Alt+Click for First Item jei.tooltip.collapsed.collapse=Alt+Click to Collapse +jei.tooltip.config.expandCollapseAll=Alt+Click Toggle Groups # Error Tooltips jei.tooltip.error.recipe.transfer.missing=Missing Items diff --git a/src/main/resources/assets/jei/lang/es_es.lang b/src/main/resources/assets/jei/lang/es_es.lang index f69752efc..6990afdbb 100644 --- a/src/main/resources/assets/jei/lang/es_es.lang +++ b/src/main/resources/assets/jei/lang/es_es.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si se debe ocultar el jei.tooltip.collapsed.expand=Alt+Clic para expandir jei.tooltip.collapsed.expand.firstItem=Alt+Clic para primer objeto jei.tooltip.collapsed.collapse=Alt+Clic para colapsar +jei.tooltip.config.expandCollapseAll=Alt+Clic alternar grupos config.jei.collapsible=Grupos colapsables config.jei.collapsible.comment=Opciones para colapsar grupos de ingredientes en una sola ranura en la lista de ingredientes. diff --git a/src/main/resources/assets/jei/lang/fi_fi.lang b/src/main/resources/assets/jei/lang/fi_fi.lang index 36b96e43b..2c52ef58c 100644 --- a/src/main/resources/assets/jei/lang/fi_fi.lang +++ b/src/main/resources/assets/jei/lang/fi_fi.lang @@ -124,7 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Piilotetaanko vasemma jei.tooltip.collapsed.expand=Alt+Klikkaa laajentaaksesi jei.tooltip.collapsed.expand.firstItem=Alt+Klikkaa ensimmäiselle esineelle jei.tooltip.collapsed.collapse=Alt+Klikkaa tiivistääksesi - +jei.tooltip.config.expandCollapseAll=Alt+Klikkaa vaihda ryhmiä config.jei.collapsible=Tiivistetyt ryhmät config.jei.collapsible.comment=Asetukset ainesosaryhmien tiivistämiseen yhdeksi paikaksi ainesosaluettelossa. config.jei.collapsible.collapsibleGroupsEnabled=Ota tiivistetyt ryhmät käyttöön diff --git a/src/main/resources/assets/jei/lang/fr_fr.lang b/src/main/resources/assets/jei/lang/fr_fr.lang index 61788051e..0258778eb 100644 --- a/src/main/resources/assets/jei/lang/fr_fr.lang +++ b/src/main/resources/assets/jei/lang/fr_fr.lang @@ -134,6 +134,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si le bouton de marqu jei.tooltip.collapsed.expand=Alt+Clic pour développer jei.tooltip.collapsed.expand.firstItem=Alt+Clic pour le premier objet jei.tooltip.collapsed.collapse=Alt+Clic pour réduire +jei.tooltip.config.expandCollapseAll=Alt+Clic basculer les groupes config.jei.collapsible=Groupes réductibles config.jei.collapsible.comment=Options pour réduire les groupes d'ingrédients en un seul emplacement dans la liste des ingrédients. diff --git a/src/main/resources/assets/jei/lang/he_il.lang b/src/main/resources/assets/jei/lang/he_il.lang index a5c244e6c..2bbb53f51 100644 --- a/src/main/resources/assets/jei/lang/he_il.lang +++ b/src/main/resources/assets/jei/lang/he_il.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=האם להסתיר jei.tooltip.collapsed.expand=Alt+לחיצה כדי להרחיב jei.tooltip.collapsed.expand.firstItem=Alt+לחיצה לפריט הראשון jei.tooltip.collapsed.collapse=Alt+לחיצה כדי לכווץ +jei.tooltip.config.expandCollapseAll=Alt+לחיצה להחלפת קבוצות config.jei.collapsible=קבוצות מתכווצות config.jei.collapsible.comment=אפשרויות לכיווץ קבוצות רכיבים לתא יחיד ברשימת הרכיבים. diff --git a/src/main/resources/assets/jei/lang/it_it.lang b/src/main/resources/assets/jei/lang/it_it.lang index f0428d3ea..9616d727b 100644 --- a/src/main/resources/assets/jei/lang/it_it.lang +++ b/src/main/resources/assets/jei/lang/it_it.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se nascondere il puls jei.tooltip.collapsed.expand=Alt+Clic per espandere jei.tooltip.collapsed.expand.firstItem=Alt+Clic per il primo oggetto jei.tooltip.collapsed.collapse=Alt+Clic per comprimere +jei.tooltip.config.expandCollapseAll=Alt+Clic attiva/disattiva gruppi config.jei.collapsible=Gruppi comprimibili config.jei.collapsible.comment=Opzioni per comprimere gruppi di ingredienti in un singolo slot nell'elenco degli ingredienti. diff --git a/src/main/resources/assets/jei/lang/ja_jp.lang b/src/main/resources/assets/jei/lang/ja_jp.lang index bb082fa97..ad5fdcaa2 100644 --- a/src/main/resources/assets/jei/lang/ja_jp.lang +++ b/src/main/resources/assets/jei/lang/ja_jp.lang @@ -134,6 +134,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=左下隅のブック jei.tooltip.collapsed.expand=Alt+クリックで展開 jei.tooltip.collapsed.expand.firstItem=Alt+クリックで最初のアイテム jei.tooltip.collapsed.collapse=Alt+クリックで折りたたむ +jei.tooltip.config.expandCollapseAll=Alt+クリックでグループ展開切替 config.jei.collapsible=折りたたみグループ config.jei.collapsible.comment=アイテムリスト内でアイテムグループをひとつのスロットに折りたたむオプション。 diff --git a/src/main/resources/assets/jei/lang/ko_kr.lang b/src/main/resources/assets/jei/lang/ko_kr.lang index dc0711cfe..d2f5c9c0c 100644 --- a/src/main/resources/assets/jei/lang/ko_kr.lang +++ b/src/main/resources/assets/jei/lang/ko_kr.lang @@ -171,6 +171,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=왼쪽 하단 모서 jei.tooltip.collapsed.expand=Alt+클릭하여 펼치기 jei.tooltip.collapsed.expand.firstItem=Alt+클릭하여 첫 번째 아이템 jei.tooltip.collapsed.collapse=Alt+클릭하여 접기 +jei.tooltip.config.expandCollapseAll=Alt+클릭으로 그룹 전환 config.jei.collapsible=접을 수 있는 그룹 config.jei.collapsible.comment=재료 목록에서 재료 그룹을 하나의 슬롯으로 접는 옵션입니다. diff --git a/src/main/resources/assets/jei/lang/lt_lt.lang b/src/main/resources/assets/jei/lang/lt_lt.lang index 69d0de74f..a71c0f776 100644 --- a/src/main/resources/assets/jei/lang/lt_lt.lang +++ b/src/main/resources/assets/jei/lang/lt_lt.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ar paslėpti žymės jei.tooltip.collapsed.expand=Alt+spustelėkite, kad išskleistumėte jei.tooltip.collapsed.expand.firstItem=Alt+spustelėkite pirmam daiktui jei.tooltip.collapsed.collapse=Alt+spustelėkite, kad sutrauktumėte +jei.tooltip.config.expandCollapseAll=Alt+Spustelėkite perjungti grupes config.jei.collapsible=Sutraukiamos grupės config.jei.collapsible.comment=Parinktys ingredientų grupių sutraukimui į vieną vietą ingredientų sąraše. diff --git a/src/main/resources/assets/jei/lang/nb_no.lang b/src/main/resources/assets/jei/lang/nb_no.lang index 9f2dfffb8..d21a1df35 100644 --- a/src/main/resources/assets/jei/lang/nb_no.lang +++ b/src/main/resources/assets/jei/lang/nb_no.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmerke-knappen i jei.tooltip.collapsed.expand=Alt+Klikk for å utvide jei.tooltip.collapsed.expand.firstItem=Alt+Klikk for første gjenstand jei.tooltip.collapsed.collapse=Alt+Klikk for å skjule +jei.tooltip.config.expandCollapseAll=Alt+Klikk for å bytte grupper config.jei.collapsible=Sammenleggbare grupper config.jei.collapsible.comment=Alternativer for å slå sammen grupper av ingredienser til én plass i ingredienslisten. diff --git a/src/main/resources/assets/jei/lang/pl_pl.lang b/src/main/resources/assets/jei/lang/pl_pl.lang index 591f99bea..b515f7cfa 100644 --- a/src/main/resources/assets/jei/lang/pl_pl.lang +++ b/src/main/resources/assets/jei/lang/pl_pl.lang @@ -137,6 +137,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Czy ukryć przycisk z jei.tooltip.collapsed.expand=Alt+Klik aby rozwinąć jei.tooltip.collapsed.expand.firstItem=Alt+Klik dla pierwszego przedmiotu jei.tooltip.collapsed.collapse=Alt+Klik aby zwinąć +jei.tooltip.config.expandCollapseAll=Alt+Klik aby przełączyć grupy config.jei.collapsible=Zwijane grupy config.jei.collapsible.comment=Opcje zwijania grup składników do jednego slotu na liście składników. diff --git a/src/main/resources/assets/jei/lang/pt_br.lang b/src/main/resources/assets/jei/lang/pt_br.lang index 7c6a8dda7..f4c919a23 100644 --- a/src/main/resources/assets/jei/lang/pt_br.lang +++ b/src/main/resources/assets/jei/lang/pt_br.lang @@ -134,6 +134,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se o botão de favori jei.tooltip.collapsed.expand=Alt+Clique para expandir jei.tooltip.collapsed.expand.firstItem=Alt+Clique para o primeiro item jei.tooltip.collapsed.collapse=Alt+Clique para recolher +jei.tooltip.config.expandCollapseAll=Alt+Clique para alternar grupos config.jei.collapsible=Grupos recolhíveis config.jei.collapsible.comment=Opções para recolher grupos de ingredientes em um único slot na lista de ingredientes. diff --git a/src/main/resources/assets/jei/lang/ru_ru.lang b/src/main/resources/assets/jei/lang/ru_ru.lang index ce24d152b..a28056a7a 100644 --- a/src/main/resources/assets/jei/lang/ru_ru.lang +++ b/src/main/resources/assets/jei/lang/ru_ru.lang @@ -169,6 +169,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Скрывать ли jei.tooltip.collapsed.expand=Alt+Клик для раскрытия jei.tooltip.collapsed.expand.firstItem=Alt+Клик для первого предмета jei.tooltip.collapsed.collapse=Alt+Клик для сворачивания +jei.tooltip.config.expandCollapseAll=Alt+Клик переключить группы config.jei.collapsible=Сворачиваемые группы config.jei.collapsible.comment=Настройки сворачивания групп ингредиентов в один слот в списке ингредиентов. diff --git a/src/main/resources/assets/jei/lang/sv_se.lang b/src/main/resources/assets/jei/lang/sv_se.lang index 11b8edeb1..74b6928e5 100644 --- a/src/main/resources/assets/jei/lang/sv_se.lang +++ b/src/main/resources/assets/jei/lang/sv_se.lang @@ -137,6 +137,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmärkesknappen jei.tooltip.collapsed.expand=Alt+Klicka för att expandera jei.tooltip.collapsed.expand.firstItem=Alt+Klicka för första föremålet jei.tooltip.collapsed.collapse=Alt+Klicka för att fälla ihop +jei.tooltip.config.expandCollapseAll=Alt+Klicka för att växla grupper config.jei.collapsible=Ihopfällbara grupper config.jei.collapsible.comment=Alternativ för att fälla ihop grupper av ingredienser till en enda plats i ingredienslistan. diff --git a/src/main/resources/assets/jei/lang/tr_tr.lang b/src/main/resources/assets/jei/lang/tr_tr.lang index 7b2a4ccde..1d8cc49f5 100644 --- a/src/main/resources/assets/jei/lang/tr_tr.lang +++ b/src/main/resources/assets/jei/lang/tr_tr.lang @@ -124,6 +124,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Sol alt köşedeki ye jei.tooltip.collapsed.expand=Alt+Tıkla genişletmek için jei.tooltip.collapsed.expand.firstItem=Alt+Tıkla ilk öğe için jei.tooltip.collapsed.collapse=Alt+Tıkla daraltmak için +jei.tooltip.config.expandCollapseAll=Alt+Tıkla Grupları Dönüştür config.jei.collapsible=Daraltılabilir Gruplar config.jei.collapsible.comment=Malzeme listesinde malzeme gruplarını tek bir yuvaya daraltma seçenekleri. diff --git a/src/main/resources/assets/jei/lang/uk_ua.lang b/src/main/resources/assets/jei/lang/uk_ua.lang index 5e9d8e49e..a69cc8091 100644 --- a/src/main/resources/assets/jei/lang/uk_ua.lang +++ b/src/main/resources/assets/jei/lang/uk_ua.lang @@ -126,6 +126,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Чи приховув jei.tooltip.collapsed.expand=Alt+Клік для розгортання jei.tooltip.collapsed.expand.firstItem=Alt+Клік для першого предмета jei.tooltip.collapsed.collapse=Alt+Клік для згортання +jei.tooltip.config.expandCollapseAll=Alt+Клік перемкнути групи config.jei.collapsible=Згортувані групи config.jei.collapsible.comment=Налаштування згортання груп інгредієнтів в один слот у списку інгредієнтів. diff --git a/src/main/resources/assets/jei/lang/zh_cn.lang b/src/main/resources/assets/jei/lang/zh_cn.lang index 4a55558c5..2cc670935 100644 --- a/src/main/resources/assets/jei/lang/zh_cn.lang +++ b/src/main/resources/assets/jei/lang/zh_cn.lang @@ -166,6 +166,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隐藏左下角 jei.tooltip.collapsed.expand=Alt+点击展开 jei.tooltip.collapsed.expand.firstItem=Alt+点击查看第一个物品 jei.tooltip.collapsed.collapse=Alt+点击收起 +jei.tooltip.config.expandCollapseAll=Alt+点击切换分组 config.jei.collapsible=可折叠分组 config.jei.collapsible.comment=将配料分组折叠为配料列表中的单个格子的相关选项。 diff --git a/src/main/resources/assets/jei/lang/zh_tw.lang b/src/main/resources/assets/jei/lang/zh_tw.lang index 31cbe60c0..015bed41b 100644 --- a/src/main/resources/assets/jei/lang/zh_tw.lang +++ b/src/main/resources/assets/jei/lang/zh_tw.lang @@ -134,6 +134,7 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隱藏左下角 jei.tooltip.collapsed.expand=Alt+點擊展開 jei.tooltip.collapsed.expand.firstItem=Alt+點擊查看第一個物品 jei.tooltip.collapsed.collapse=Alt+點擊收合 +jei.tooltip.config.expandCollapseAll=Alt+點擊切換群組 config.jei.collapsible=可折疊群組 config.jei.collapsible.comment=將材料群組折疊為材料列表中的單個格子的相關選項。 From cf984a62cbb1717ddc1a57879673ca39c82d7a17 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 14 Mar 2026 02:18:49 -0400 Subject: [PATCH 22/48] Implement fluidstacks & 1 item groups as itemstack Implement fluidstack in groups. Implement single item groups act as itemstack. --- .../mezz/jei/gui/overlay/IngredientGrid.java | 4 +- .../collapsible/GuiCollapsibleGroups.java | 79 ++++---- .../collapsible/GuiCustomGroupEditor.java | 180 +++++++++--------- .../ingredients/CollapsibleEntryRegistry.java | 20 +- 4 files changed, 157 insertions(+), 126 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java index 72ec8c9a6..e7d51584b 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java @@ -198,7 +198,9 @@ public boolean handleMouseClicked(int mouseX, int mouseY) { boolean expandKeyDown = firstItemMode ? altDown : !altDown; if (expandKeyDown) { CollapsedStackRenderer collapsedHovered = guiIngredientSlots.getHoveredCollapsed(mouseX, mouseY); - if (collapsedHovered != null) { + // A group with only 1 visible item should act as a plain ingredient click, + // not expand/collapse — the single item is already trivially "shown". + if (collapsedHovered != null && collapsedHovered.getCollapsedStack().size() > 1) { collapsedHovered.getCollapsedStack().toggleExpanded(); Internal.getIngredientFilter().notifyCollapsedStateChanged(); return true; diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index e66897372..86273db02 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -1,11 +1,10 @@ package mezz.jei.gui.overlay.collapsible; import mezz.jei.Internal; -import mezz.jei.api.ingredients.IIngredientHelper; +import mezz.jei.api.ingredients.IIngredientRenderer; import mezz.jei.config.Config; import mezz.jei.config.CustomGroupsConfig; import mezz.jei.gui.ingredients.IIngredientListElement; -import mezz.jei.ingredients.CollapsedStack; import mezz.jei.ingredients.CollapsibleEntry; import mezz.jei.ingredients.CollapsibleEntryRegistry; import mezz.jei.ingredients.IngredientFilter; @@ -15,6 +14,7 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; import net.minecraftforge.fml.client.config.GuiUtils; import org.lwjgl.input.Mouse; @@ -58,7 +58,7 @@ public class GuiCollapsibleGroups extends GuiScreen { private final List cardEntries = new ArrayList<>(); private int currentPage = 0; private int totalPages = 1; - @Nullable private ItemStack tooltipStack = null; + @Nullable private IIngredientListElement tooltipElement = null; // Drag-to-scroll state for card preview boxes private int dragCardAbsIdx = -1; @@ -107,7 +107,7 @@ private void rebuildCards() { // Custom groups come first (like REI) for (CollapsibleEntry entry : registry.getCustomEntries()) { - List previewItems = getPreviewItems(entry); + List> previewItems = getPreviewItems(entry); int itemCount = getMatchedItemCount(entry); cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), true, !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); @@ -115,7 +115,7 @@ private void rebuildCards() { // Default groups for (CollapsibleEntry entry : registry.getEntries()) { - List previewItems = getPreviewItems(entry); + List> previewItems = getPreviewItems(entry); int itemCount = getMatchedItemCount(entry); cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), false, !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); @@ -281,7 +281,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { int startIdx = currentPage * cardsPerPage; int endIdx = Math.min(startIdx + cardsPerPage, cardEntries.size()); - tooltipStack = null; + tooltipElement = null; for (int i = startIdx; i < endIdx; i++) { int localIdx = i - startIdx; int col = localIdx / cardsPerCol; @@ -306,8 +306,8 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { super.drawScreen(mouseX, mouseY, partialTicks); - if (tooltipStack != null) { - renderToolTip(tooltipStack, mouseX, mouseY); + if (tooltipElement != null) { + renderIngredientTooltip(tooltipElement, mouseX, mouseY); } } @@ -348,15 +348,15 @@ private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, RenderHelper.enableGUIStandardItemLighting(); GlStateManager.enableDepth(); for (int i = firstItem; i < lastItem; i++) { - ItemStack stack = card.previewItems.get(i); + IIngredientListElement element = card.previewItems.get(i); int visibleRow = (i / PREVIEW_COLS) - scrollRow; int col = i % PREVIEW_COLS; int itemX = previewX + col * slotSize; int itemY = previewY + visibleRow * slotSize; - this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, itemX, itemY); + renderIngredient(element, itemX + 1, itemY + 1); if (mouseX >= itemX && mouseX < itemX + PREVIEW_SIZE && mouseY >= itemY && mouseY < itemY + PREVIEW_SIZE) { - tooltipStack = stack; + tooltipElement = element; } } RenderHelper.disableStandardItemLighting(); @@ -487,44 +487,53 @@ protected void keyTyped(char typedChar, int keyCode) throws IOException { } /** - * Get up to PREVIEW_COLS * PREVIEW_ROWS preview ItemStacks for a collapsible entry. - * Non-ItemStack ingredients (e.g. EnchantmentData) are converted via the ingredient - * helper's getCheatItemStack so enchanted books render correctly. + * Get up to PREVIEW_FETCH_MAX preview elements for a collapsible entry, + * returning the raw IIngredientListElement so each type renders via its own renderer. */ - private List getPreviewItems(CollapsibleEntry entry) { - List items = new ArrayList<>(); + private List> getPreviewItems(CollapsibleEntry entry) { + List> items = new ArrayList<>(); if (!Internal.hasIngredientFilter()) { return items; } IngredientFilter filter = Internal.getIngredientFilter(); List ingredientList = filter.getIngredientList(""); - int maxItems = PREVIEW_FETCH_MAX; for (IIngredientListElement element : ingredientList) { if (entry.matches(element)) { - ItemStack preview = toPreviewStack(element); - if (preview != null && !preview.isEmpty()) { - items.add(preview); - if (items.size() >= maxItems) { - break; - } + items.add(element); + if (items.size() >= PREVIEW_FETCH_MAX) { + break; } } } return items; } - /** Converts any ingredient to a renderable ItemStack. Returns null if not possible. */ - @Nullable - private static ItemStack toPreviewStack(IIngredientListElement element) { - V ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack) { - return (ItemStack) ingredient; + @SuppressWarnings("unchecked") + private void renderIngredient(IIngredientListElement element, int x, int y) { + try { + IIngredientRenderer renderer = element.getIngredientRenderer(); + renderer.render(this.mc, x, y, element.getIngredient()); + } catch (Exception ignored) { } + } + + @SuppressWarnings("unchecked") + private void renderIngredientTooltip(IIngredientListElement element, int mouseX, int mouseY) { try { - IIngredientHelper helper = Internal.getIngredientRegistry().getIngredientHelper(ingredient); - return helper.getCheatItemStack(ingredient); - } catch (Exception e) { - return null; + T ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + renderToolTip((ItemStack) ingredient, mouseX, mouseY); + return; + } + IIngredientRenderer renderer = element.getIngredientRenderer(); + List tooltip = renderer.getTooltip(this.mc, ingredient, + this.mc.gameSettings.advancedItemTooltips + ? ITooltipFlag.TooltipFlags.ADVANCED + : ITooltipFlag.TooltipFlags.NORMAL); + if (!tooltip.isEmpty()) { + drawHoveringText(tooltip, mouseX, mouseY, renderer.getFontRenderer(this.mc, ingredient)); + } + } catch (Exception ignored) { } } @@ -559,11 +568,11 @@ private static class GroupCardEntry { final String displayName; final boolean isCustom; boolean enabled; - final List previewItems; + final List> previewItems; final int itemCount; int previewScrollRow = 0; - GroupCardEntry(String id, String displayName, boolean isCustom, boolean enabled, List previewItems, int itemCount) { + GroupCardEntry(String id, String displayName, boolean isCustom, boolean enabled, List> previewItems, int itemCount) { this.id = id; this.displayName = displayName; this.isCustom = isCustom; diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index 497702983..b74130e26 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -14,6 +14,7 @@ import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; import net.minecraftforge.fml.client.config.GuiUtils; import org.lwjgl.input.Keyboard; @@ -68,8 +69,8 @@ public class GuiCustomGroupEditor extends GuiScreen { private int rightTotalPages = 1; private int rightItemsPerPage; - // Cached selected stacks for the right panel - private List selectedStacks = new ArrayList<>(); + // Cached selected elements for the right panel (any ingredient type) + private List> selectedStacks = new ArrayList<>(); // Drag-select state private boolean isDragging = false; @@ -144,8 +145,55 @@ public void onGuiClosed() { Keyboard.enableRepeatEvents(false); } - private void updateFilteredItems() { - if (!Internal.hasIngredientFilter()) { + /** + * Returns a unique string identifier for any ingredient type, or null if unavailable. + * Uses StackHelper for ItemStacks, and the generic IngredientRegistry helper for everything else + * (e.g. FluidStack via FluidStackHelper.getUniqueId). + */ + @Nullable + private static String getIngredientUid(Object ingredient) { + if (ingredient instanceof ItemStack) { + ItemStack stack = (ItemStack) ingredient; + if (stack.isEmpty()) return null; + try { + return Internal.getStackHelper().getUniqueIdentifierForStack(stack); + } catch (Exception e) { + return null; + } + } + try { + @SuppressWarnings("unchecked") + mezz.jei.api.ingredients.IIngredientHelper helper = + (mezz.jei.api.ingredients.IIngredientHelper) + Internal.getIngredientRegistry().getIngredientHelper(ingredient); + return helper.getUniqueId(ingredient); + } catch (Exception e) { + return null; + } + } + + /** Renders a tooltip for any ingredient type, falling back to ItemStack rendering for items. */ + @SuppressWarnings("unchecked") + private void renderIngredientTooltip(IIngredientListElement element, int mouseX, int mouseY) { + try { + T ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack) { + renderToolTip((ItemStack) ingredient, mouseX, mouseY); + return; + } + IIngredientRenderer renderer = element.getIngredientRenderer(); + List tooltip = renderer.getTooltip(this.mc, ingredient, + this.mc.gameSettings.advancedItemTooltips + ? ITooltipFlag.TooltipFlags.ADVANCED + : ITooltipFlag.TooltipFlags.NORMAL); + if (!tooltip.isEmpty()) { + drawHoveringText(tooltip, mouseX, mouseY, renderer.getFontRenderer(this.mc, ingredient)); + } + } catch (Exception ignored) { + } + } + + private void updateFilteredItems() { if (!Internal.hasIngredientFilter()) { filteredItems = Collections.emptyList(); return; } @@ -163,23 +211,13 @@ private void updateSelectedStacks() { if (!Internal.hasIngredientFilter()) { return; } - StackHelper stackHelper = Internal.getStackHelper(); IngredientFilter filter = Internal.getIngredientFilter(); - // Get the full ingredient list and find items matching selectedUids + // Get the full ingredient list and find elements matching selectedUids (any ingredient type) List all = filter.getIngredientList(""); for (IIngredientListElement element : all) { - Object ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack) { - ItemStack stack = (ItemStack) ingredient; - if (!stack.isEmpty()) { - try { - String uid = stackHelper.getUniqueIdentifierForStack(stack); - if (selectedUids.contains(uid)) { - selectedStacks.add(stack); - } - } catch (Exception ignored) { - } - } + String uid = getIngredientUid(element.getIngredient()); + if (uid != null && selectedUids.contains(uid)) { + selectedStacks.add(element); } } rightTotalPages = Math.max(1, (selectedStacks.size() + rightItemsPerPage - 1) / rightItemsPerPage); @@ -296,7 +334,6 @@ private void drawLeftGrid(int mouseX, int mouseY) { if (filteredItems.isEmpty()) { return; } - StackHelper stackHelper = Internal.getStackHelper(); int startIdx = leftPage * leftItemsPerPage; RenderHelper.enableGUIStandardItemLighting(); @@ -313,20 +350,15 @@ private void drawLeftGrid(int mouseX, int mouseY) { renderIngredient(element, x + 1, y + 1); // Green overlay if selected - if (ingredient instanceof ItemStack) { - try { - String uid = stackHelper.getUniqueIdentifierForStack((ItemStack) ingredient); - if (selectedUids.contains(uid)) { - RenderHelper.disableStandardItemLighting(); - GlStateManager.disableDepth(); - GlStateManager.colorMask(true, true, true, false); - GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x4000FF00, 0x4000FF00); - GlStateManager.colorMask(true, true, true, true); - GlStateManager.enableDepth(); - RenderHelper.enableGUIStandardItemLighting(); - } - } catch (Exception ignored) { - } + String uid = getIngredientUid(ingredient); + if (uid != null && selectedUids.contains(uid)) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + GlStateManager.colorMask(true, true, true, false); + GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x4000FF00, 0x4000FF00); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableDepth(); + RenderHelper.enableGUIStandardItemLighting(); } // Highlight on hover @@ -355,13 +387,13 @@ private void drawRightGrid(int mouseX, int mouseY) { GlStateManager.enableDepth(); for (int i = 0; i < rightItemsPerPage && (startIdx + i) < selectedStacks.size(); i++) { - ItemStack stack = selectedStacks.get(startIdx + i); + IIngredientListElement element = selectedStacks.get(startIdx + i); int col = i % rightCols; int row = i / rightCols; int x = rightGridX + col * ITEM_SIZE; int y = rightGridY + row * ITEM_SIZE; - this.mc.getRenderItem().renderItemAndEffectIntoGUI(stack, x + 1, y + 1); + renderIngredient(element, x + 1, y + 1); // Highlight on hover if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { @@ -400,11 +432,7 @@ private void drawLeftGridTooltips(int mouseX, int mouseY) { int x = leftGridX + col * ITEM_SIZE; int y = leftGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - IIngredientListElement element = filteredItems.get(startIdx + i); - Object ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack) { - renderToolTip((ItemStack) ingredient, mouseX, mouseY); - } + renderIngredientTooltip(filteredItems.get(startIdx + i), mouseX, mouseY); return; } } @@ -421,7 +449,7 @@ private void drawRightGridTooltips(int mouseX, int mouseY) { int x = rightGridX + col * ITEM_SIZE; int y = rightGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - renderToolTip(selectedStacks.get(startIdx + i), mouseX, mouseY); + renderIngredientTooltip(selectedStacks.get(startIdx + i), mouseX, mouseY); return; } } @@ -452,16 +480,12 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx int y = leftGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { IIngredientListElement element = filteredItems.get(startIdx + i); - Object ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack) { - ItemStack stack = (ItemStack) ingredient; - try { - String uid = Internal.getStackHelper().getUniqueIdentifierForStack(stack); - dragAdding = !selectedUids.contains(uid); - isDragging = true; - lastDraggedUid = uid; - } catch (Exception ignored) {} - toggleSelection(stack); + String uid = getIngredientUid(element.getIngredient()); + if (uid != null) { + dragAdding = !selectedUids.contains(uid); + isDragging = true; + lastDraggedUid = uid; + toggleSelectionByUid(uid); } return; } @@ -477,8 +501,9 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx int x = rightGridX + col * ITEM_SIZE; int y = rightGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - ItemStack stack = selectedStacks.get(startIdx + i); - removeSelection(stack); + IIngredientListElement element = selectedStacks.get(startIdx + i); + String uid = getIngredientUid(element.getIngredient()); + if (uid != null) removeSelectionByUid(uid); return; } } @@ -505,47 +530,32 @@ protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, lo int x = leftGridX + col * ITEM_SIZE; int y = leftGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - Object ingredient = filteredItems.get(startIdx + i).getIngredient(); - if (ingredient instanceof ItemStack) { - try { - String uid = Internal.getStackHelper().getUniqueIdentifierForStack((ItemStack) ingredient); - if (!uid.equals(lastDraggedUid)) { - lastDraggedUid = uid; - if (dragAdding) { - if (selectedUids.add(uid)) updateSelectedStacks(); - } else { - if (selectedUids.remove(uid)) updateSelectedStacks(); - } - } - } catch (Exception ignored) {} + String uid = getIngredientUid(filteredItems.get(startIdx + i).getIngredient()); + if (uid != null && !uid.equals(lastDraggedUid)) { + lastDraggedUid = uid; + if (dragAdding) { + if (selectedUids.add(uid)) updateSelectedStacks(); + } else { + if (selectedUids.remove(uid)) updateSelectedStacks(); + } } return; } } } - private void toggleSelection(ItemStack stack) { - try { - StackHelper stackHelper = Internal.getStackHelper(); - String uid = stackHelper.getUniqueIdentifierForStack(stack); - if (selectedUids.contains(uid)) { - selectedUids.remove(uid); - } else { - selectedUids.add(uid); - } - updateSelectedStacks(); - } catch (Exception ignored) { + private void toggleSelectionByUid(String uid) { + if (selectedUids.contains(uid)) { + selectedUids.remove(uid); + } else { + selectedUids.add(uid); } + updateSelectedStacks(); } - private void removeSelection(ItemStack stack) { - try { - StackHelper stackHelper = Internal.getStackHelper(); - String uid = stackHelper.getUniqueIdentifierForStack(stack); - selectedUids.remove(uid); - updateSelectedStacks(); - } catch (Exception ignored) { - } + private void removeSelectionByUid(String uid) { + selectedUids.remove(uid); + updateSelectedStacks(); } @Override diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java index 374d0ad00..22b7aa222 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java @@ -7,7 +7,6 @@ import mezz.jei.startup.StackHelper; import mezz.jei.util.Log; import net.minecraft.item.ItemStack; - import javax.annotation.Nullable; import java.util.*; import java.util.function.Predicate; @@ -106,11 +105,22 @@ public void loadCustomGroups() { } Set uidSet = new HashSet<>(group.itemUids); String displayName = group.displayName != null ? group.displayName : group.id; - // Custom groups only ever contain ItemStacks identified by UID. - customEntries.add(CollapsibleEntry.ofItemStack(group.id, displayName, stack -> { + // Matcher works for both ItemStack and non-ItemStack ingredients (e.g. FluidStack): + // for ItemStacks use StackHelper, for everything else use the generic IngredientRegistry helper. + customEntries.add(new CollapsibleEntry(group.id, displayName, ingredient -> { try { - StackHelper stackHelper = Internal.getStackHelper(); - String uid = stackHelper.getUniqueIdentifierForStack(stack); + String uid; + if (ingredient instanceof ItemStack) { + ItemStack stack = (ItemStack) ingredient; + if (stack.isEmpty()) return false; + uid = Internal.getStackHelper().getUniqueIdentifierForStack(stack); + } else { + @SuppressWarnings("unchecked") + mezz.jei.api.ingredients.IIngredientHelper helper = + (mezz.jei.api.ingredients.IIngredientHelper) + Internal.getIngredientRegistry().getIngredientHelper(ingredient); + uid = helper.getUniqueId(ingredient); + } return uidSet.contains(uid); } catch (Exception e) { return false; From 7fc95faafc217913d5580f1ea21d3f63ce533a15 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 15 Mar 2026 02:26:36 -0400 Subject: [PATCH 23/48] Merge CollapsedStack and CollapsibleEntry. Merge CollapsedStack and CollapsibleEntry classes. Registered CollapsedStack.TYPE Changed CollapsedStack Ingredient Renderer to implement IIngredientRenderer<> CollapsedStackIngredientHelper implements IIngredientHelper<> --- src/main/java/mezz/jei/Internal.java | 18 +-- .../java/mezz/jei/gui/GuiEventHandler.java | 4 +- .../mezz/jei/gui/overlay/ConfigButton.java | 14 +- .../collapsible/GuiCollapsibleGroups.java | 18 +-- .../collapsible/GuiCustomGroupEditor.java | 2 +- .../mezz/jei/ingredients/CollapsedStack.java | 101 +++++++++---- .../CollapsedStackIngredientHelper.java | 136 ++++++++++++++++++ .../CollapsedStackIngredientRenderer.java | 100 +++++++++++++ ...istry.java => CollapsedStackRegistry.java} | 30 ++-- .../jei/ingredients/CollapsibleEntry.java | 74 ---------- .../jei/ingredients/IngredientFilter.java | 50 +++---- .../jei/plugins/jei/JEIInternalPlugin.java | 8 ++ .../jei/render/CollapsedStackRenderer.java | 13 +- .../java/mezz/jei/startup/JeiStarter.java | 6 +- 14 files changed, 398 insertions(+), 176 deletions(-) create mode 100644 src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java create mode 100644 src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java rename src/main/java/mezz/jei/ingredients/{CollapsibleEntryRegistry.java => CollapsedStackRegistry.java} (79%) delete mode 100644 src/main/java/mezz/jei/ingredients/CollapsibleEntry.java diff --git a/src/main/java/mezz/jei/Internal.java b/src/main/java/mezz/jei/Internal.java index e3a37ad0d..d63f0449b 100644 --- a/src/main/java/mezz/jei/Internal.java +++ b/src/main/java/mezz/jei/Internal.java @@ -5,7 +5,7 @@ import mezz.jei.bookmarks.BookmarkList; import mezz.jei.color.ColorNamer; import mezz.jei.gui.GuiEventHandler; -import mezz.jei.ingredients.CollapsibleEntryRegistry; +import mezz.jei.ingredients.CollapsedStackRegistry; import mezz.jei.ingredients.IngredientFilter; import mezz.jei.ingredients.IngredientRegistry; import mezz.jei.input.InputHandler; @@ -42,7 +42,7 @@ public final class Internal { @Nullable private static BookmarkList bookmarkList; @Nullable - private static CollapsibleEntryRegistry collapsibleEntryRegistry; + private static CollapsedStackRegistry collapsedStackRegistry; private Internal() { @@ -155,15 +155,15 @@ public static BookmarkList getBookmarkList() { return bookmarkList; } - public static CollapsibleEntryRegistry getCollapsibleEntryRegistry() { - if (collapsibleEntryRegistry == null) { - collapsibleEntryRegistry = CollapsibleEntryRegistry.getInstance(); + public static CollapsedStackRegistry getCollapsedStackRegistry() { + if (collapsedStackRegistry == null) { + collapsedStackRegistry = CollapsedStackRegistry.getInstance(); } - return collapsibleEntryRegistry; + return collapsedStackRegistry; } - public static void setCollapsibleEntryRegistry(CollapsibleEntryRegistry registry) { - Internal.collapsibleEntryRegistry = registry; - CollapsibleEntryRegistry.setInstance(registry); + public static void setCollapsedStackRegistry(CollapsedStackRegistry registry) { + Internal.collapsedStackRegistry = registry; + CollapsedStackRegistry.setInstance(registry); } } diff --git a/src/main/java/mezz/jei/gui/GuiEventHandler.java b/src/main/java/mezz/jei/gui/GuiEventHandler.java index 94f92cd97..81208cae2 100644 --- a/src/main/java/mezz/jei/gui/GuiEventHandler.java +++ b/src/main/java/mezz/jei/gui/GuiEventHandler.java @@ -3,7 +3,7 @@ import mezz.jei.Internal; import mezz.jei.config.Config; import mezz.jei.config.OverlayToggleEvent; -import mezz.jei.ingredients.CollapsibleEntryRegistry; +import mezz.jei.ingredients.CollapsedStackRegistry; import mezz.jei.gui.ghost.GhostIngredientDragManager; import mezz.jei.gui.overlay.IngredientListOverlay; import mezz.jei.gui.overlay.bookmarks.LeftAreaDispatcher; @@ -65,7 +65,7 @@ public void onGuiOpen(GuiOpenEvent event) { ghostIngredientDragManager.updateScreen(gui, false); if (wasDisplayed && !ingredientListOverlay.isListDisplayed() && Config.isCollapseOnClose() && Internal.hasIngredientFilter()) { - CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); registry.getEntries().forEach(e -> e.setExpanded(false)); registry.getCustomEntries().forEach(e -> e.setExpanded(false)); Internal.getIngredientFilter().notifyCollapsedStateChanged(); diff --git a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java index 09e89faeb..d3a8ef836 100644 --- a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java +++ b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java @@ -15,8 +15,8 @@ import mezz.jei.config.KeyBindings; import mezz.jei.gui.GuiHelper; import mezz.jei.gui.elements.GuiIconToggleButton; -import mezz.jei.ingredients.CollapsibleEntry; -import mezz.jei.ingredients.CollapsibleEntryRegistry; +import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.ingredients.CollapsedStackRegistry; import mezz.jei.util.Translator; import org.lwjgl.input.Keyboard; @@ -67,11 +67,11 @@ protected boolean onMouseClicked(int mouseX, int mouseY) { if (Config.isOverlayEnabled()) { if (GuiScreen.isAltKeyDown() && Config.isCollapsibleGroupsEnabled() && Internal.hasIngredientFilter()) { - CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); - Collection entries = registry.getEntries(); - List customEntries = registry.getCustomEntries(); - boolean allExpanded = entries.stream().allMatch(CollapsibleEntry::isExpanded) - && customEntries.stream().allMatch(CollapsibleEntry::isExpanded); + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); + Collection entries = registry.getEntries(); + List customEntries = registry.getCustomEntries(); + boolean allExpanded = entries.stream().allMatch(CollapsedStack::isExpanded) + && customEntries.stream().allMatch(CollapsedStack::isExpanded); boolean targetExpanded = !allExpanded; entries.forEach(e -> e.setExpanded(targetExpanded)); customEntries.forEach(e -> e.setExpanded(targetExpanded)); diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 86273db02..df2d7338d 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -5,8 +5,8 @@ import mezz.jei.config.Config; import mezz.jei.config.CustomGroupsConfig; import mezz.jei.gui.ingredients.IIngredientListElement; -import mezz.jei.ingredients.CollapsibleEntry; -import mezz.jei.ingredients.CollapsibleEntryRegistry; +import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.ingredients.CollapsedStackRegistry; import mezz.jei.ingredients.IngredientFilter; import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; @@ -103,10 +103,10 @@ public void initGui() { private void rebuildCards() { cardEntries.clear(); - CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); // Custom groups come first (like REI) - for (CollapsibleEntry entry : registry.getCustomEntries()) { + for (CollapsedStack entry : registry.getCustomEntries()) { List> previewItems = getPreviewItems(entry); int itemCount = getMatchedItemCount(entry); cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), true, @@ -114,7 +114,7 @@ private void rebuildCards() { } // Default groups - for (CollapsibleEntry entry : registry.getEntries()) { + for (CollapsedStack entry : registry.getEntries()) { List> previewItems = getPreviewItems(entry); int itemCount = getMatchedItemCount(entry); cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), false, @@ -202,7 +202,7 @@ protected void actionPerformed(GuiButton button) throws IOException { GroupCardEntry card = cardEntries.get(idx); card.enabled = !card.enabled; - CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); Set disabled = new HashSet<>(registry.getDisabledGroups()); if (card.enabled) { disabled.remove(card.id); @@ -253,7 +253,7 @@ protected void actionPerformed(GuiButton button) throws IOException { CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); if (customGroupsConfig != null) { customGroupsConfig.removeGroup(card.id); - Internal.getCollapsibleEntryRegistry().recollectCustomEntries(); + Internal.getCollapsedStackRegistry().recollectCustomEntries(); if (Internal.hasIngredientFilter()) { IngredientFilter filter = Internal.getIngredientFilter(); @@ -490,7 +490,7 @@ protected void keyTyped(char typedChar, int keyCode) throws IOException { * Get up to PREVIEW_FETCH_MAX preview elements for a collapsible entry, * returning the raw IIngredientListElement so each type renders via its own renderer. */ - private List> getPreviewItems(CollapsibleEntry entry) { + private List> getPreviewItems(CollapsedStack entry) { List> items = new ArrayList<>(); if (!Internal.hasIngredientFilter()) { return items; @@ -540,7 +540,7 @@ private void renderIngredientTooltip(IIngredientListElement element, int /** * Count matched items for display. */ - private int getMatchedItemCount(CollapsibleEntry entry) { + private int getMatchedItemCount(CollapsedStack entry) { if (!Internal.hasIngredientFilter()) { return 0; } diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index b74130e26..57ad46aeb 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -259,7 +259,7 @@ private void saveAndClose() { CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); if (customGroupsConfig != null) { customGroupsConfig.updateGroup(group); - Internal.getCollapsibleEntryRegistry().recollectCustomEntries(); + Internal.getCollapsedStackRegistry().recollectCustomEntries(); if (Internal.hasIngredientFilter()) { IngredientFilter filter = Internal.getIngredientFilter(); // Invalidate the filter cache so the next call to getIngredientList diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStack.java b/src/main/java/mezz/jei/ingredients/CollapsedStack.java index 0799bc25d..26e22db5e 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStack.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStack.java @@ -1,59 +1,108 @@ package mezz.jei.ingredients; +import mezz.jei.api.recipe.IIngredientType; import mezz.jei.gui.ingredients.IIngredientListElement; +import net.minecraft.item.ItemStack; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; /** - * Represents a collapsed group in a specific filter result. - * Pairs a {@link CollapsibleEntry} definition with the actual matched ingredients - * from the current filtered list. - * - * This is NOT an {@link IIngredientListElement} — it coexists with them in a mixed list. + * Represents a collapsible ingredient group — both the group definition (id, display name, + * matcher, expanded state) and the runtime list of matched ingredients from the current filter. + *

+ * Registered as an {@link IIngredientType} so that addons which introspect grid items + * always find a valid type. Recipe lookups are delegated to the first ingredient via + * {@link mezz.jei.api.ingredients.IIngredientHelper#translateFocus}. + *

*/ public class CollapsedStack { - private final CollapsibleEntry entry; + // Registered as IIngredientType for addon compatibility — addons expect every grid item to have a type + public static final IIngredientType TYPE = () -> CollapsedStack.class; + + private final String id; + private final String displayName; + /** Matches against the raw ingredient object (any type). */ + private final Predicate matcher; + private boolean expanded; private final List> ingredients; - public CollapsedStack(CollapsibleEntry entry) { - this.entry = entry; + /** + * Primary constructor — matcher receives the raw ingredient object. + */ + public CollapsedStack(String id, String displayName, Predicate matcher) { + this.id = id; + this.displayName = displayName; + this.matcher = matcher; + this.expanded = false; this.ingredients = new ArrayList<>(); } - public CollapsibleEntry getEntry() { - return entry; + /** + * Convenience factory for groups that only care about ItemStack ingredients. + * Non-ItemStack ingredients automatically return false. + */ + public static CollapsedStack ofItemStack(String id, String displayName, Predicate stackMatcher) { + return new CollapsedStack(id, displayName, + ingredient -> ingredient instanceof ItemStack && stackMatcher.test((ItemStack) ingredient)); } - public List> getIngredients() { - return ingredients; + // --- Group definition --- + + public String getId() { + return id; } - public void addIngredient(IIngredientListElement element) { - ingredients.add(element); + public String getDisplayName() { + return displayName; } - public int size() { - return ingredients.size(); + public boolean isExpanded() { + return expanded; } - public boolean isEmpty() { - return ingredients.isEmpty(); + public void setExpanded(boolean expanded) { + this.expanded = expanded; } - public String getDisplayName() { - return entry.getDisplayName(); + public void toggleExpanded() { + this.expanded = !this.expanded; } - public boolean isExpanded() { - return entry.isExpanded(); + /** + * Tests whether the given element matches this collapsible group. + * The raw ingredient (any type) is passed to the matcher. + * Empty ItemStacks are always rejected. + */ + public boolean matches(IIngredientListElement element) { + Object ingredient = element.getIngredient(); + if (ingredient instanceof ItemStack && ((ItemStack) ingredient).isEmpty()) { + return false; + } + return matcher.test(ingredient); } - public void setExpanded(boolean expanded) { - entry.setExpanded(expanded); + // --- Runtime ingredient list (transient per filter cycle) --- + + public List> getIngredients() { + return ingredients; } - public void toggleExpanded() { - entry.toggleExpanded(); + public void addIngredient(IIngredientListElement element) { + ingredients.add(element); + } + + /** Clears the transient ingredient list for reuse across filter recalculations. */ + public void clearIngredients() { + ingredients.clear(); + } + + public int size() { + return ingredients.size(); + } + + public boolean isEmpty() { + return ingredients.isEmpty(); } } diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java new file mode 100644 index 000000000..a92a7b06e --- /dev/null +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java @@ -0,0 +1,136 @@ +package mezz.jei.ingredients; + +import mezz.jei.Internal; +import mezz.jei.api.ingredients.IIngredientHelper; +import mezz.jei.api.recipe.IFocus; +import mezz.jei.gui.ingredients.IIngredientListElement; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nullable; +import java.awt.*; +import java.util.Collection; +import java.util.Collections; + +/** + * IIngredientHelper for the CollapsedStack ingredient type. + * Follows the BookmarkIngredientHelper delegation pattern — most methods + * delegate to the first ingredient's helper for addon compatibility. + */ +public class CollapsedStackIngredientHelper implements IIngredientHelper { + @Nullable + @Override + public CollapsedStack getMatch(Iterable ingredients, CollapsedStack ingredientToMatch) { + for (CollapsedStack cs : ingredients) { + if (cs.getId().equals(ingredientToMatch.getId())) { + return cs; + } + } + return null; + } + + @Override + public String getDisplayName(CollapsedStack ingredient) { + return ingredient.getDisplayName(); + } + + @Override + public String getUniqueId(CollapsedStack ingredient) { + // Prefixed to avoid collisions with other ingredient type UIDs + return "collapsedstack:" + ingredient.getId(); + } + + @Override + public String getWildcardId(CollapsedStack ingredient) { + return getUniqueId(ingredient); + } + + @Override + public String getModId(CollapsedStack ingredient) { + return getFirstIngredientHelper(ingredient).getModId(getFirstIngredient(ingredient)); + } + + @Override + public String getDisplayModId(CollapsedStack ingredient) { + return getFirstIngredientHelper(ingredient).getDisplayModId(getFirstIngredient(ingredient)); + } + + @Override + public Iterable getColors(CollapsedStack ingredient) { + return getFirstIngredientHelper(ingredient).getColors(getFirstIngredient(ingredient)); + } + + @Override + public String getResourceId(CollapsedStack ingredient) { + return "collapsedstack:" + ingredient.getId(); + } + + @Override + public ItemStack getCheatItemStack(CollapsedStack ingredient) { + // Delegate cheat-give to the first ingredient in the group + return getFirstIngredientHelper(ingredient).getCheatItemStack(getFirstIngredient(ingredient)); + } + + @Override + public CollapsedStack copyIngredient(CollapsedStack ingredient) { + // CollapsedStack instances are transient and shared via the registry per filter cycle; + // returning the same instance is safe for current usage patterns. + return ingredient; + } + + @Override + public boolean isValidIngredient(CollapsedStack ingredient) { + return !ingredient.isEmpty(); + } + + @Override + public boolean isIngredientOnServer(CollapsedStack ingredient) { + return true; + } + + @Override + public Collection getOreDictNames(CollapsedStack ingredient) { + return Collections.emptyList(); + } + + @Override + public Collection getCreativeTabNames(CollapsedStack ingredient) { + return Collections.emptyList(); + } + + @Override + public String getErrorInfo(@Nullable CollapsedStack ingredient) { + if (ingredient == null) { + return "CollapsedStack is null"; + } + return "CollapsedStack[" + ingredient.getId() + ", " + ingredient.size() + " items]"; + } + + // Delegate recipe lookups to the representative item for addon compatibility + @Override + public IFocus translateFocus(IFocus focus, IFocusFactory focusFactory) { + CollapsedStack cs = focus.getValue(); + if (cs != null && !cs.isEmpty()) { + Object firstIngredient = cs.getIngredients().get(0).getIngredient(); + return focusFactory.createFocus(focus.getMode(), firstIngredient); + } + return focus; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static IIngredientHelper getFirstIngredientHelper(CollapsedStack ingredient) { + if (ingredient.isEmpty()) { + // Fallback: return ItemStack helper as a safe default + return Internal.getIngredientRegistry().getIngredientHelper(ItemStack.class); + } + Object first = ingredient.getIngredients().get(0).getIngredient(); + return Internal.getIngredientRegistry().getIngredientHelper(first); + } + + @SuppressWarnings("rawtypes") + private static Object getFirstIngredient(CollapsedStack ingredient) { + if (ingredient.isEmpty()) { + return ItemStack.EMPTY; + } + return ingredient.getIngredients().get(0).getIngredient(); + } +} diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java new file mode 100644 index 000000000..66af681b2 --- /dev/null +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java @@ -0,0 +1,100 @@ +package mezz.jei.ingredients; + +import mezz.jei.api.ingredients.IIngredientRenderer; +import mezz.jei.gui.ingredients.IIngredientListElement; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderItem; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +/** + * IIngredientRenderer for the CollapsedStack ingredient type. + * Handles the type-system 16×16 render contract; the grid-specific overlay + * rendering (area/padding/tooltip/highlight) remains in CollapsedStackRenderer. + */ +public class CollapsedStackIngredientRenderer implements IIngredientRenderer { + @Override + public void render(Minecraft minecraft, int xPosition, int yPosition, @Nullable CollapsedStack ingredient) { + if (ingredient == null || ingredient.isEmpty()) { + return; + } + + List> ingredients = ingredient.getIngredients(); + + // Subtle background tint to visually distinguish collapsed groups + GuiScreen.drawRect(xPosition, yPosition, xPosition + 16, yPosition + 16, 0x33FFFFFF); + + if (ingredients.size() == 1) { + renderElementAt(minecraft, ingredients.get(0), xPosition, yPosition, 1.0f); + } else { + // Stacked-card icon: back item upper-right, front item lower-left, both at 0.75× scale + RenderItem renderItem = minecraft.getRenderItem(); + renderElementAt(minecraft, ingredients.get(1), xPosition + 4, yPosition, 0.75f); + float prevZLevel = renderItem.zLevel; + renderItem.zLevel += 100; + renderElementAt(minecraft, ingredients.get(0), xPosition, yPosition + 4, 0.75f); + renderItem.zLevel = prevZLevel; + } + + // Count badge + int count = ingredient.size(); + if (count > 1) { + FontRenderer fontRenderer = minecraft.fontRenderer; + String countStr = String.valueOf(count); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + GlStateManager.disableBlend(); + final float badgeScale = 0.75f; + int textWidth = fontRenderer.getStringWidth(countStr); + int scaledRight = (int) ((xPosition + 16) / badgeScale); + int scaledTop = (int) ((yPosition + 10) / badgeScale); + GlStateManager.pushMatrix(); + GlStateManager.scale(badgeScale, badgeScale, 1.0f); + fontRenderer.drawStringWithShadow(countStr, scaledRight - textWidth, scaledTop, 0xFFAA00); + GlStateManager.popMatrix(); + GlStateManager.enableDepth(); + } + + GlStateManager.disableLighting(); + GlStateManager.color(1, 1, 1, 1); + } + + @Override + public List getTooltip(Minecraft minecraft, CollapsedStack ingredient, ITooltipFlag tooltipFlag) { + List tooltip = new ArrayList<>(); + tooltip.add(net.minecraft.util.text.TextFormatting.GOLD + ingredient.getDisplayName() + + net.minecraft.util.text.TextFormatting.GRAY + " (" + ingredient.size() + " items)"); + return tooltip; + } + + @Override + public FontRenderer getFontRenderer(Minecraft minecraft, CollapsedStack ingredient) { + return minecraft.fontRenderer; + } + + @SuppressWarnings("unchecked") + private static void renderElementAt(Minecraft minecraft, IIngredientListElement element, int x, int y, float scale) { + Object ingredient = element.getIngredient(); + try { + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y, 0); + GlStateManager.scale(scale, scale, scale); + if (ingredient instanceof ItemStack) { + minecraft.getRenderItem().renderItemAndEffectIntoGUI((ItemStack) ingredient, 0, 0); + } else { + IIngredientRenderer renderer = element.getIngredientRenderer(); + renderer.render(minecraft, 0, 0, ingredient); + } + GlStateManager.popMatrix(); + } catch (RuntimeException | LinkageError ignored) { + GlStateManager.popMatrix(); + } + } +} diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java similarity index 79% rename from src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java rename to src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index 22b7aa222..09d39ab94 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsibleEntryRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -12,25 +12,25 @@ import java.util.function.Predicate; /** - * Registry holding all collapsible entry group definitions. + * Registry for CollapsedStack group definitions. * Groups are checked in registration order; first match wins. */ -public class CollapsibleEntryRegistry { +public class CollapsedStackRegistry { @Nullable - private static CollapsibleEntryRegistry instance; + private static CollapsedStackRegistry instance; - private final LinkedHashMap entries = new LinkedHashMap<>(); - private final List customEntries = new ArrayList<>(); + private final LinkedHashMap entries = new LinkedHashMap<>(); + private final List customEntries = new ArrayList<>(); private final Set disabledGroups = new HashSet<>(); - public static CollapsibleEntryRegistry getInstance() { + public static CollapsedStackRegistry getInstance() { if (instance == null) { - instance = new CollapsibleEntryRegistry(); + instance = new CollapsedStackRegistry(); } return instance; } - public static void setInstance(@Nullable CollapsibleEntryRegistry registry) { + public static void setInstance(@Nullable CollapsedStackRegistry registry) { instance = registry; } @@ -43,7 +43,7 @@ public static void setInstance(@Nullable CollapsibleEntryRegistry registry) { * @param matcher predicate that returns true for ItemStacks belonging to this group */ public void group(String id, String displayName, Predicate matcher) { - entries.put(id, CollapsibleEntry.ofItemStack(id, displayName, matcher)); + entries.put(id, CollapsedStack.ofItemStack(id, displayName, matcher)); } /** @@ -56,15 +56,15 @@ public void group(String id, String displayName, Predicate matcher) { * @param matcher predicate on the raw ingredient object */ public void groupForType(String id, String displayName, Predicate matcher) { - entries.put(id, new CollapsibleEntry(id, displayName, matcher)); + entries.put(id, new CollapsedStack(id, displayName, matcher)); } - public Collection getEntries() { + public Collection getEntries() { return entries.values(); } @Nullable - public CollapsibleEntry getEntry(String id) { + public CollapsedStack getEntry(String id) { return entries.get(id); } @@ -85,13 +85,13 @@ public boolean isGroupEnabled(String id) { return !disabledGroups.contains(id); } - public List getCustomEntries() { + public List getCustomEntries() { return customEntries; } /** * Load custom collapsible groups from the JSON config. - * Creates CollapsibleEntry objects that match items by their unique identifier. + * Creates CollapsedStack objects that match items by their unique identifier. */ public void loadCustomGroups() { customEntries.clear(); @@ -107,7 +107,7 @@ public void loadCustomGroups() { String displayName = group.displayName != null ? group.displayName : group.id; // Matcher works for both ItemStack and non-ItemStack ingredients (e.g. FluidStack): // for ItemStacks use StackHelper, for everything else use the generic IngredientRegistry helper. - customEntries.add(new CollapsibleEntry(group.id, displayName, ingredient -> { + customEntries.add(new CollapsedStack(group.id, displayName, ingredient -> { try { String uid; if (ingredient instanceof ItemStack) { diff --git a/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java b/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java deleted file mode 100644 index 1d146aef4..000000000 --- a/src/main/java/mezz/jei/ingredients/CollapsibleEntry.java +++ /dev/null @@ -1,74 +0,0 @@ -package mezz.jei.ingredients; - -import mezz.jei.gui.ingredients.IIngredientListElement; -import net.minecraft.item.ItemStack; - -import java.util.function.Predicate; - -/** - * Defines a collapsible group of ingredients in the ingredient list. - * When collapsed, all matching ingredients are shown as a single entry. - * When expanded, all matching ingredients appear individually. - * - * The matcher operates on the raw ingredient object so that non-ItemStack - * ingredient types (e.g. EnchantmentData for enchanted books) can be matched. - */ -public class CollapsibleEntry { - private final String id; - private final String displayName; - /** Matches against the raw ingredient object (any type). */ - private final Predicate matcher; - private boolean expanded; - - /** - * Primary constructor — matcher receives the raw ingredient object. - */ - public CollapsibleEntry(String id, String displayName, Predicate matcher) { - this.id = id; - this.displayName = displayName; - this.matcher = matcher; - this.expanded = false; - } - - /** - * Convenience constructor for groups that only care about ItemStack ingredients. - * Non-ItemStack ingredients automatically return false. - */ - public static CollapsibleEntry ofItemStack(String id, String displayName, Predicate stackMatcher) { - return new CollapsibleEntry(id, displayName, - ingredient -> ingredient instanceof ItemStack && stackMatcher.test((ItemStack) ingredient)); - } - - public String getId() { - return id; - } - - public String getDisplayName() { - return displayName; - } - - public boolean isExpanded() { - return expanded; - } - - public void setExpanded(boolean expanded) { - this.expanded = expanded; - } - - public void toggleExpanded() { - this.expanded = !this.expanded; - } - - /** - * Tests whether the given element matches this collapsible group. - * The raw ingredient (any type) is passed to the matcher. - * Empty ItemStacks are always rejected. - */ - public boolean matches(IIngredientListElement element) { - Object ingredient = element.getIngredient(); - if (ingredient instanceof ItemStack && ((ItemStack) ingredient).isEmpty()) { - return false; - } - return matcher.test(ingredient); - } -} diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index d538bad7e..4d1ba8fbd 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -146,11 +146,11 @@ public void onEditModeToggleEvent(EditModeToggleEvent event) { // In Hide Ingredients Mode the user cannot Alt+Click to expand/collapse groups, // so expand all groups when entering edit mode and collapse them on exit. boolean editMode = event.isEditModeEnabled(); - CollapsibleEntryRegistry registry = mezz.jei.Internal.getCollapsibleEntryRegistry(); - for (CollapsibleEntry entry : registry.getEntries()) { + CollapsedStackRegistry registry = mezz.jei.Internal.getCollapsedStackRegistry(); + for (CollapsedStack entry : registry.getEntries()) { entry.setExpanded(editMode); } - for (CollapsibleEntry entry : registry.getCustomEntries()) { + for (CollapsedStack entry : registry.getCustomEntries()) { entry.setExpanded(editMode); } this.collapsedListCached = Collections.emptyList(); @@ -276,14 +276,14 @@ private List> getIngredientListUncached(String filterT @SuppressWarnings("unchecked") private List> withGroupNameMatches( List> baseList, String filterText) { - CollapsibleEntryRegistry registry = CollapsibleEntryRegistry.getInstance(); - List matchingGroups = new ArrayList<>(); - for (CollapsibleEntry entry : registry.getEntries()) { + CollapsedStackRegistry registry = CollapsedStackRegistry.getInstance(); + List matchingGroups = new ArrayList<>(); + for (CollapsedStack entry : registry.getEntries()) { if (Translator.toLowercaseWithLocale(entry.getDisplayName()).contains(filterText)) { matchingGroups.add(entry); } } - for (CollapsibleEntry entry : registry.getCustomEntries()) { + for (CollapsedStack entry : registry.getCustomEntries()) { if (Translator.toLowercaseWithLocale(entry.getDisplayName()).contains(filterText)) { matchingGroups.add(entry); } @@ -299,7 +299,7 @@ private List> withGroupNameMatches( if (!element.isVisible() || seen.contains(element)) { continue; } - for (CollapsibleEntry entry : matchingGroups) { + for (CollapsedStack entry : matchingGroups) { if (entry.matches(element)) { result.add(element); seen.add(element); @@ -314,28 +314,28 @@ private List> withGroupNameMatches( /** * Converts a flat filtered ingredient list into a mixed list containing * both individual IIngredientListElement objects and CollapsedStack groups. - * Each ingredient is assigned to the first matching CollapsibleEntry (first match wins). + * Each ingredient is assigned to the first matching CollapsedStack group (first match wins). * If collapsible groups are disabled, returns the original list cast to List<Object>. */ private List collapse(List ingredientList) { if (!Config.isCollapsibleGroupsEnabled()) { return new ArrayList<>(ingredientList); } - CollapsibleEntryRegistry registry = CollapsibleEntryRegistry.getInstance(); - Collection entries = registry.getEntries(); - List customEntries = registry.getCustomEntries(); + CollapsedStackRegistry registry = CollapsedStackRegistry.getInstance(); + Collection entries = registry.getEntries(); + List customEntries = registry.getCustomEntries(); if (entries.isEmpty() && customEntries.isEmpty()) { return new ArrayList<>(ingredientList); } // Build the list of active entries (not disabled) - List activeEntries = new ArrayList<>(); - for (CollapsibleEntry entry : entries) { + List activeEntries = new ArrayList<>(); + for (CollapsedStack entry : entries) { if (registry.isGroupEnabled(entry.getId())) { activeEntries.add(entry); } } - for (CollapsibleEntry entry : customEntries) { + for (CollapsedStack entry : customEntries) { if (registry.isGroupEnabled(entry.getId())) { activeEntries.add(entry); } @@ -344,21 +344,23 @@ private List collapse(List ingredientList) { return new ArrayList<>(ingredientList); } - // Map from entry -> CollapsedStack (created on first match) - Map collapsedMap = new LinkedHashMap<>(); + // CollapsedStack serves as both group definition and runtime container; + // clear transient ingredients before repopulating from the current filter. + for (CollapsedStack entry : activeEntries) { + entry.clearIngredients(); + } List result = new ArrayList<>(ingredientList.size()); + // Track which entries have already been added to the result list + Set addedToResult = Collections.newSetFromMap(new IdentityHashMap<>()); for (IIngredientListElement element : ingredientList) { boolean matched = false; - for (CollapsibleEntry entry : activeEntries) { + for (CollapsedStack entry : activeEntries) { if (entry.matches(element)) { - CollapsedStack collapsed = collapsedMap.get(entry); - if (collapsed == null) { - collapsed = new CollapsedStack(entry); - collapsedMap.put(entry, collapsed); - result.add(collapsed); + if (addedToResult.add(entry)) { + result.add(entry); } - collapsed.addIngredient(element); + entry.addIngredient(element); matched = true; } } diff --git a/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java b/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java index 7fe00822d..17968f365 100644 --- a/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java +++ b/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java @@ -9,6 +9,9 @@ import mezz.jei.bookmarks.BookmarkIngredientHelper; import mezz.jei.bookmarks.BookmarkItem; import mezz.jei.bookmarks.BookmarkItemRender; +import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.ingredients.CollapsedStackIngredientHelper; +import mezz.jei.ingredients.CollapsedStackIngredientRenderer; import net.minecraftforge.fml.common.registry.ForgeRegistries; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; @@ -60,6 +63,11 @@ public void registerIngredients(IModIngredientRegistration ingredientRegistratio BookmarkIngredientHelper bookmarkIngredientHelper = new BookmarkIngredientHelper(); BookmarkItemRender bookmarkItemRender = new BookmarkItemRender(); ingredientRegistration.register(BookmarkItem.TYPE, Collections.emptyList(), bookmarkIngredientHelper, bookmarkItemRender); + + // Register CollapsedStack as ingredient type — addons that introspect grid items require a registered type + CollapsedStackIngredientHelper csHelper = new CollapsedStackIngredientHelper(); + CollapsedStackIngredientRenderer csRenderer = new CollapsedStackIngredientRenderer(); + ingredientRegistration.register(CollapsedStack.TYPE, Collections.emptyList(), csHelper, csRenderer); } @Override diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index c0f5400c1..bc8bba4c1 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -1,10 +1,11 @@ package mezz.jei.render; import mezz.jei.api.ingredients.IIngredientRenderer; -import mezz.jei.gui.TooltipRenderer; +import mezz.jei.config.Config; import mezz.jei.gui.ingredients.IIngredientListElement; import mezz.jei.ingredients.CollapsedStack; import mezz.jei.input.ClickedIngredient; +import mezz.jei.util.CollapsedClickAction; import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; @@ -179,7 +180,7 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { // In OPEN_GROUP mode, alt+click uses first item; show that as the hint. // In FIRST_ITEM mode, alt+click expands; show that instead. String hint = TextFormatting.YELLOW + Translator.translateToLocal( - mezz.jei.config.Config.getCollapsedClickAction() == mezz.jei.util.CollapsedClickAction.OPEN_GROUP + Config.getCollapsedClickAction() == CollapsedClickAction.OPEN_GROUP ? "jei.tooltip.collapsed.expand.firstItem" : "jei.tooltip.collapsed.expand"); @@ -254,8 +255,8 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { } /** - * Gets a ClickedIngredient for the first item in the group, - * so that recipe lookups still work for the representative item. + * Returns the CollapsedStack as the clicked ingredient — registered as IIngredientType + * for addon compatibility. Recipe lookups are delegated via translateFocus on the helper. */ @Nullable public ClickedIngredient getClickedIngredient() { @@ -263,8 +264,8 @@ public ClickedIngredient getClickedIngredient() { if (ingredients.isEmpty()) { return null; } - IIngredientListElement first = ingredients.get(0); - return ClickedIngredient.create(first.getIngredient(), area); + // Return CollapsedStack directly — it is a registered IIngredientType + return ClickedIngredient.create(collapsedStack, area); } public boolean isMouseOver(int mouseX, int mouseY) { diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index 5004fb6ab..c0c873cf2 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -12,7 +12,7 @@ import mezz.jei.bookmarks.BookmarkList; import mezz.jei.config.Config; import mezz.jei.gui.GuiEventHandler; -import mezz.jei.ingredients.CollapsibleEntryRegistry; +import mezz.jei.ingredients.CollapsedStackRegistry; import mezz.jei.gui.GuiHelper; import mezz.jei.gui.GuiScreenHelper; import mezz.jei.gui.ghost.GhostIngredientDragManager; @@ -103,7 +103,7 @@ public void load(List plugins, Textures textures, boolean recipesOnl registerDefaultCollapsibleGroups(); { - CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); registry.loadCustomGroups(); registry.syncDisabledGroups(); } @@ -348,7 +348,7 @@ private static void sendRuntime(List plugins, IJeiRuntime jeiRuntime } private static void registerDefaultCollapsibleGroups() { - CollapsibleEntryRegistry registry = Internal.getCollapsibleEntryRegistry(); + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); // Enchanted books in JEI are stored as EnchantmentData (VanillaTypes.ENCHANT), // not as ItemStacks — IngredientRegistry strips them from the ItemStack list. // Match all EnchantmentData directly; every EnchantmentData IS an enchanted book. From 740c473bef21341f06290dbb3d002b07447b7bef Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 15 Mar 2026 02:38:25 -0400 Subject: [PATCH 24/48] CollpasedStack use native Renderer CollapsedStackRenderer implements IIngredientRenderer<> --- .../CollapsedStackIngredientRenderer.java | 100 ------------------ .../jei/plugins/jei/JEIInternalPlugin.java | 5 +- .../jei/render/CollapsedStackRenderer.java | 49 +++++++-- 3 files changed, 41 insertions(+), 113 deletions(-) delete mode 100644 src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java deleted file mode 100644 index 66af681b2..000000000 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientRenderer.java +++ /dev/null @@ -1,100 +0,0 @@ -package mezz.jei.ingredients; - -import mezz.jei.api.ingredients.IIngredientRenderer; -import mezz.jei.gui.ingredients.IIngredientListElement; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.RenderItem; -import net.minecraft.client.util.ITooltipFlag; -import net.minecraft.item.ItemStack; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * IIngredientRenderer for the CollapsedStack ingredient type. - * Handles the type-system 16×16 render contract; the grid-specific overlay - * rendering (area/padding/tooltip/highlight) remains in CollapsedStackRenderer. - */ -public class CollapsedStackIngredientRenderer implements IIngredientRenderer { - @Override - public void render(Minecraft minecraft, int xPosition, int yPosition, @Nullable CollapsedStack ingredient) { - if (ingredient == null || ingredient.isEmpty()) { - return; - } - - List> ingredients = ingredient.getIngredients(); - - // Subtle background tint to visually distinguish collapsed groups - GuiScreen.drawRect(xPosition, yPosition, xPosition + 16, yPosition + 16, 0x33FFFFFF); - - if (ingredients.size() == 1) { - renderElementAt(minecraft, ingredients.get(0), xPosition, yPosition, 1.0f); - } else { - // Stacked-card icon: back item upper-right, front item lower-left, both at 0.75× scale - RenderItem renderItem = minecraft.getRenderItem(); - renderElementAt(minecraft, ingredients.get(1), xPosition + 4, yPosition, 0.75f); - float prevZLevel = renderItem.zLevel; - renderItem.zLevel += 100; - renderElementAt(minecraft, ingredients.get(0), xPosition, yPosition + 4, 0.75f); - renderItem.zLevel = prevZLevel; - } - - // Count badge - int count = ingredient.size(); - if (count > 1) { - FontRenderer fontRenderer = minecraft.fontRenderer; - String countStr = String.valueOf(count); - GlStateManager.disableLighting(); - GlStateManager.disableDepth(); - GlStateManager.disableBlend(); - final float badgeScale = 0.75f; - int textWidth = fontRenderer.getStringWidth(countStr); - int scaledRight = (int) ((xPosition + 16) / badgeScale); - int scaledTop = (int) ((yPosition + 10) / badgeScale); - GlStateManager.pushMatrix(); - GlStateManager.scale(badgeScale, badgeScale, 1.0f); - fontRenderer.drawStringWithShadow(countStr, scaledRight - textWidth, scaledTop, 0xFFAA00); - GlStateManager.popMatrix(); - GlStateManager.enableDepth(); - } - - GlStateManager.disableLighting(); - GlStateManager.color(1, 1, 1, 1); - } - - @Override - public List getTooltip(Minecraft minecraft, CollapsedStack ingredient, ITooltipFlag tooltipFlag) { - List tooltip = new ArrayList<>(); - tooltip.add(net.minecraft.util.text.TextFormatting.GOLD + ingredient.getDisplayName() - + net.minecraft.util.text.TextFormatting.GRAY + " (" + ingredient.size() + " items)"); - return tooltip; - } - - @Override - public FontRenderer getFontRenderer(Minecraft minecraft, CollapsedStack ingredient) { - return minecraft.fontRenderer; - } - - @SuppressWarnings("unchecked") - private static void renderElementAt(Minecraft minecraft, IIngredientListElement element, int x, int y, float scale) { - Object ingredient = element.getIngredient(); - try { - GlStateManager.pushMatrix(); - GlStateManager.translate(x, y, 0); - GlStateManager.scale(scale, scale, scale); - if (ingredient instanceof ItemStack) { - minecraft.getRenderItem().renderItemAndEffectIntoGUI((ItemStack) ingredient, 0, 0); - } else { - IIngredientRenderer renderer = element.getIngredientRenderer(); - renderer.render(minecraft, 0, 0, ingredient); - } - GlStateManager.popMatrix(); - } catch (RuntimeException | LinkageError ignored) { - GlStateManager.popMatrix(); - } - } -} diff --git a/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java b/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java index 17968f365..3c3460628 100644 --- a/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java +++ b/src/main/java/mezz/jei/plugins/jei/JEIInternalPlugin.java @@ -11,7 +11,7 @@ import mezz.jei.bookmarks.BookmarkItemRender; import mezz.jei.ingredients.CollapsedStack; import mezz.jei.ingredients.CollapsedStackIngredientHelper; -import mezz.jei.ingredients.CollapsedStackIngredientRenderer; +import mezz.jei.render.CollapsedStackRenderer; import net.minecraftforge.fml.common.registry.ForgeRegistries; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; @@ -66,8 +66,7 @@ public void registerIngredients(IModIngredientRegistration ingredientRegistratio // Register CollapsedStack as ingredient type — addons that introspect grid items require a registered type CollapsedStackIngredientHelper csHelper = new CollapsedStackIngredientHelper(); - CollapsedStackIngredientRenderer csRenderer = new CollapsedStackIngredientRenderer(); - ingredientRegistration.register(CollapsedStack.TYPE, Collections.emptyList(), csHelper, csRenderer); + ingredientRegistration.register(CollapsedStack.TYPE, Collections.emptyList(), csHelper, CollapsedStackRenderer.INSTANCE); } @Override diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index bc8bba4c1..457d132e9 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -14,6 +14,7 @@ import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.RenderItem; +import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.client.config.GuiUtils; @@ -28,10 +29,13 @@ * Shows the first item with a count badge indicating total group size, * plus a semi-transparent background to distinguish it from normal items. */ -public class CollapsedStackRenderer { +public class CollapsedStackRenderer implements IIngredientRenderer { private static final int COLLAPSED_BG_COLOR = 0x33FFFFFF; private static final int COLLAPSED_BORDER_COLOR = 0x55AAAAFF; + /** Singleton registered with the ingredient type system — {@code collapsedStack} is null. */ + public static final CollapsedStackRenderer INSTANCE = new CollapsedStackRenderer(null); + private final CollapsedStack collapsedStack; private Rectangle area = new Rectangle(0, 0, 16, 16); private int padding; @@ -56,23 +60,29 @@ public Rectangle getArea() { return area; } + /** Grid overlay render — uses this instance's stack and area+padding. */ + public void render(Minecraft minecraft) { + if (collapsedStack == null || collapsedStack.isEmpty()) { + return; + } + renderAt(minecraft, collapsedStack, area.x + padding, area.y + padding); + } + /** - * Renders the collapsed group as a single slot. + * Stateless render at an arbitrary position — shared by the instance render and + * the {@link IIngredientRenderer} contract. * For groups with 2+ items, mimics REI's stacked-card icon: each item is rendered at * 0.75× scale (12 px), offset 4 px so both stay entirely within the 16×16 slot area. * Back item (upper-right): screen origin (x+4, y+0), occupies (x+4..x+16, y..y+12) * Front item (lower-left) : screen origin (x+0, y+4), occupies (x..x+12, y+4..y+16) * Count badge is drawn at 0.75× scale in orange in the bottom-right corner. */ - public void render(Minecraft minecraft) { - List> ingredients = collapsedStack.getIngredients(); + private static void renderAt(Minecraft minecraft, CollapsedStack ingredient, int x, int y) { + List> ingredients = ingredient.getIngredients(); if (ingredients.isEmpty()) { return; } - int x = area.x + padding; - int y = area.y + padding; - // Draw background tint to visually distinguish collapsed groups GuiScreen.drawRect(x, y, x + 16, y + 16, COLLAPSED_BG_COLOR); @@ -95,7 +105,7 @@ public void render(Minecraft minecraft) { } // Count badge: 0.75× scale, orange, right-aligned at the bottom of the slot - int count = collapsedStack.size(); + int count = ingredient.size(); if (count > 1) { FontRenderer fontRenderer = minecraft.fontRenderer; String countStr = String.valueOf(count); @@ -123,7 +133,7 @@ public void render(Minecraft minecraft) { * Renders one ingredient at (x, y) at the given scale using the GL matrix stack. * Delegates to renderItemAndEffectIntoGUI so all item types (2D, 3D, built-in) render correctly. */ - private void renderElementAt(Minecraft minecraft, IIngredientListElement element, int x, int y, float scale) { + private static void renderElementAt(Minecraft minecraft, IIngredientListElement element, int x, int y, float scale) { Object ingredient = element.getIngredient(); try { GlStateManager.pushMatrix(); @@ -140,7 +150,7 @@ private void renderElementAt(Minecraft minecraft, IIngredientListElement elem } } - private void drawCollapsedBorder(int x, int y) { + private static void drawCollapsedBorder(int x, int y) { // Small triangle indicator in the top-left corner to show it's collapsible GlStateManager.disableLighting(); GlStateManager.disableDepth(); @@ -149,6 +159,25 @@ private void drawCollapsedBorder(int x, int y) { GlStateManager.enableDepth(); } + // --- IIngredientRenderer implementation --- + // INSTANCE (null stack) is registered with the ingredient type system. + + @Override + public void render(Minecraft minecraft, int xPosition, int yPosition, @Nullable CollapsedStack ingredient) { + if (ingredient == null || ingredient.isEmpty()) { + return; + } + renderAt(minecraft, ingredient, xPosition, yPosition); + } + + @Override + public List getTooltip(Minecraft minecraft, CollapsedStack ingredient, ITooltipFlag tooltipFlag) { + List tooltip = new ArrayList<>(); + tooltip.add(TextFormatting.GOLD + ingredient.getDisplayName() + + TextFormatting.GRAY + " (" + ingredient.size() + " items)"); + return tooltip; + } + public void drawHighlight() { GlStateManager.disableLighting(); GlStateManager.disableDepth(); From 1ee068fbc23249143377171675c9f7a02e502978 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 15 Mar 2026 04:47:52 -0400 Subject: [PATCH 25/48] Implement item selector wildcard & feedback Implements wildcard selections item selector Implements badges for wildcards Implements orange tints for grouped items in other groups Implements tooltips in item selector --- .../collapsible/GuiCustomGroupEditor.java | 233 ++++++++++++++++-- .../ingredients/CollapsedStackRegistry.java | 18 +- 2 files changed, 226 insertions(+), 25 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index 57ad46aeb..b93c093aa 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -16,6 +16,7 @@ import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; +import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.client.config.GuiUtils; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; @@ -71,10 +72,20 @@ public class GuiCustomGroupEditor extends GuiScreen { // Cached selected elements for the right panel (any ingredient type) private List> selectedStacks = new ArrayList<>(); + // Maps each right-panel element to its stored UID (exact or wildcard ending in ":*") + private final Map, String> selectedStackToStoredUid = new HashMap<>(); + + // Index of OTHER custom groups — used to tint items that already belong to a different group. + // Built once in initGui(); key = exact UID, value = list of display names. + private Map> otherGroupExactUids = new HashMap<>(); + // [prefix, displayName] pairs for wildcard entries in other groups + private List otherGroupWildcardPrefixes = new ArrayList<>(); // Drag-select state private boolean isDragging = false; private boolean dragAdding = false; + /** True when the current drag was started with Ctrl held — uses wildcard UIDs. */ + private boolean dragWildcard = false; @Nullable private String lastDraggedUid = null; public GuiCustomGroupEditor(GuiCollapsibleGroups parentScreen, CustomGroupsConfig.CustomGroup group) { @@ -137,6 +148,7 @@ public void initGui() { updateFilteredItems(); updateSelectedStacks(); + buildOtherGroupIndex(); } @Override @@ -172,24 +184,103 @@ private static String getIngredientUid(Object ingredient) { } } - /** Renders a tooltip for any ingredient type, falling back to ItemStack rendering for items. */ + /** + * Returns a wildcard UID for ItemStacks by stripping metadata/subtype info, then appending ":*" + * as a storage marker to distinguish it from exact UIDs. + * Non-ItemStack ingredients have no metadata variants, so their normal UID is returned. + */ + @Nullable + private static String getIngredientWildcardUid(Object ingredient) { + if (ingredient instanceof ItemStack) { + ItemStack stack = (ItemStack) ingredient; + if (stack.isEmpty()) return null; + try { + String prefix = Internal.getStackHelper().getUniqueIdentifierForStack(stack, StackHelper.UidMode.WILDCARD); + return prefix + ":*"; + } catch (Exception e) { + return null; + } + } + // Non-ItemStack types have no metadata variants — fall back to exact UID + return getIngredientUid(ingredient); + } + + /** + * Returns true if the given normal UID is covered by any selected entry — + * either an exact match, or a wildcard prefix match (stored as "prefix:*"). + */ + private boolean isUidSelected(String normalUid) { + if (selectedUids.contains(normalUid)) return true; + return findCoveringWildcard(normalUid) != null; + } + + /** + * Returns the wildcard stored UID (ending in ":*") that covers the given normal UID, + * or null if no wildcard covers it. + */ + @Nullable + /** Builds a reverse index of all other custom groups for fast membership lookup. */ + private void buildOtherGroupIndex() { + otherGroupExactUids.clear(); + otherGroupWildcardPrefixes.clear(); + CustomGroupsConfig cfg = Config.getCustomGroupsConfig(); + if (cfg == null) return; + for (CustomGroupsConfig.CustomGroup g : cfg.getCustomGroups()) { + if (g.id.equals(group.id) || g.itemUids == null) continue; + String name = (g.displayName != null && !g.displayName.isEmpty()) ? g.displayName : g.id; + for (String uid : g.itemUids) { + if (uid.endsWith(":*")) { + otherGroupWildcardPrefixes.add(new String[]{uid.substring(0, uid.length() - 2), name}); + } else { + otherGroupExactUids.computeIfAbsent(uid, k -> new ArrayList<>()).add(name); + } + } + } + } + + /** Returns the names of other custom groups that contain the given normal UID, or an empty list. */ + private List getOtherGroupNames(String normalUid) { + List names = new ArrayList<>(otherGroupExactUids.getOrDefault(normalUid, Collections.emptyList())); + for (String[] entry : otherGroupWildcardPrefixes) { + if (normalUid.equals(entry[0]) || normalUid.startsWith(entry[0] + ":")) { + names.add(entry[1]); + } + } + return names; + } + + private String findCoveringWildcard(String normalUid) { + for (String stored : selectedUids) { + if (stored.endsWith(":*")) { + String prefix = stored.substring(0, stored.length() - 2); + if (normalUid.equals(prefix) || normalUid.startsWith(prefix + ":")) return prefix + ":*"; + } + } + return null; + } + + /** Returns a mutable list of tooltip lines for any ingredient element. */ @SuppressWarnings("unchecked") - private void renderIngredientTooltip(IIngredientListElement element, int mouseX, int mouseY) { + private List getIngredientTooltipLines(IIngredientListElement element) { try { T ingredient = element.getIngredient(); + ITooltipFlag flag = mc.gameSettings.advancedItemTooltips + ? ITooltipFlag.TooltipFlags.ADVANCED + : ITooltipFlag.TooltipFlags.NORMAL; if (ingredient instanceof ItemStack) { - renderToolTip((ItemStack) ingredient, mouseX, mouseY); - return; + return ((ItemStack) ingredient).getTooltip(mc.player, flag); } IIngredientRenderer renderer = element.getIngredientRenderer(); - List tooltip = renderer.getTooltip(this.mc, ingredient, - this.mc.gameSettings.advancedItemTooltips - ? ITooltipFlag.TooltipFlags.ADVANCED - : ITooltipFlag.TooltipFlags.NORMAL); - if (!tooltip.isEmpty()) { - drawHoveringText(tooltip, mouseX, mouseY, renderer.getFontRenderer(this.mc, ingredient)); - } - } catch (Exception ignored) { + return new ArrayList<>(renderer.getTooltip(mc, ingredient, flag)); + } catch (Exception e) { + return new ArrayList<>(); + } + } + + private void renderIngredientTooltip(IIngredientListElement element, int mouseX, int mouseY) { + List lines = getIngredientTooltipLines(element); + if (!lines.isEmpty()) { + drawHoveringText(lines, mouseX, mouseY, mc.fontRenderer); } } @@ -208,18 +299,42 @@ private void updateFilteredItems() { if (!Internal.hasIngredientFilter()) { private void updateSelectedStacks() { selectedStacks.clear(); + selectedStackToStoredUid.clear(); if (!Internal.hasIngredientFilter()) { return; } IngredientFilter filter = Internal.getIngredientFilter(); - // Get the full ingredient list and find elements matching selectedUids (any ingredient type) List all = filter.getIngredientList(""); + + // Precompute wildcard prefix → stored UID for O(n) matching + Map wildcardPrefixToStored = new LinkedHashMap<>(); + for (String uid : selectedUids) { + if (uid.endsWith(":*")) { + wildcardPrefixToStored.put(uid.substring(0, uid.length() - 2), uid); + } + } + for (IIngredientListElement element : all) { String uid = getIngredientUid(element.getIngredient()); - if (uid != null && selectedUids.contains(uid)) { + if (uid == null) continue; + if (selectedUids.contains(uid)) { + // Exact match selectedStacks.add(element); + selectedStackToStoredUid.put(element, uid); + } else if (!wildcardPrefixToStored.isEmpty()) { + // Wildcard prefix match — show ALL matching elements + for (Map.Entry entry : wildcardPrefixToStored.entrySet()) { + String prefix = entry.getKey(); + String storedUid = entry.getValue(); + if (uid.equals(prefix) || uid.startsWith(prefix + ":")) { + selectedStacks.add(element); + selectedStackToStoredUid.put(element, storedUid); + break; + } + } } } + rightTotalPages = Math.max(1, (selectedStacks.size() + rightItemsPerPage - 1) / rightItemsPerPage); if (rightPage >= rightTotalPages) { rightPage = rightTotalPages - 1; @@ -349,14 +464,30 @@ private void drawLeftGrid(int mouseX, int mouseY) { Object ingredient = element.getIngredient(); renderIngredient(element, x + 1, y + 1); - // Green overlay if selected + // Orange tint if this item belongs to another custom group String uid = getIngredientUid(ingredient); - if (uid != null && selectedUids.contains(uid)) { + if (uid != null && !getOtherGroupNames(uid).isEmpty()) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + GlStateManager.colorMask(true, true, true, false); + GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x40FF8800, 0x40FF8800); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.enableDepth(); + RenderHelper.enableGUIStandardItemLighting(); + } + + // Green overlay if selected (exact or wildcard) + boolean selected = uid != null && isUidSelected(uid); + if (selected) { RenderHelper.disableStandardItemLighting(); GlStateManager.disableDepth(); GlStateManager.colorMask(true, true, true, false); GuiUtils.drawGradientRect(0, x, y, x + ITEM_SIZE, y + ITEM_SIZE, 0x4000FF00, 0x4000FF00); GlStateManager.colorMask(true, true, true, true); + // "*" badge for wildcard-matched items (covered by a stored ":*" uid, not exact) + if (uid != null && !selectedUids.contains(uid)) { + fontRenderer.drawStringWithShadow("*", x + 1, y + 1, 0xFFAA00); + } GlStateManager.enableDepth(); RenderHelper.enableGUIStandardItemLighting(); } @@ -395,6 +526,16 @@ private void drawRightGrid(int mouseX, int mouseY) { renderIngredient(element, x + 1, y + 1); + // "*" badge if this is a wildcard representative entry + String storedUid = selectedStackToStoredUid.get(element); + if (storedUid != null && storedUid.endsWith(":*")) { + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableDepth(); + fontRenderer.drawStringWithShadow("*", x + 1, y + 1, 0xFFAA00); + GlStateManager.enableDepth(); + RenderHelper.enableGUIStandardItemLighting(); + } + // Highlight on hover if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { RenderHelper.disableStandardItemLighting(); @@ -432,7 +573,22 @@ private void drawLeftGridTooltips(int mouseX, int mouseY) { int x = leftGridX + col * ITEM_SIZE; int y = leftGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - renderIngredientTooltip(filteredItems.get(startIdx + i), mouseX, mouseY); + IIngredientListElement element = filteredItems.get(startIdx + i); + List lines = getIngredientTooltipLines(element); + if (element.getIngredient() instanceof ItemStack) { + lines.add(TextFormatting.GOLD + "Ctrl+Click: Wildcard (*)"); + } + String uid2 = getIngredientUid(element.getIngredient()); + if (uid2 != null) { + List otherGroups = getOtherGroupNames(uid2); + if (!otherGroups.isEmpty()) { + lines.add(TextFormatting.GOLD + "In group" + (otherGroups.size() > 1 ? "s" : "") + ": " + + String.join(", ", otherGroups)); + } + } + if (!lines.isEmpty()) { + drawHoveringText(lines, mouseX, mouseY, mc.fontRenderer); + } return; } } @@ -449,7 +605,15 @@ private void drawRightGridTooltips(int mouseX, int mouseY) { int x = rightGridX + col * ITEM_SIZE; int y = rightGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - renderIngredientTooltip(selectedStacks.get(startIdx + i), mouseX, mouseY); + IIngredientListElement element = selectedStacks.get(startIdx + i); + String storedUid = selectedStackToStoredUid.get(element); + List lines = getIngredientTooltipLines(element); + if (storedUid != null && storedUid.endsWith(":*")) { + lines.add(TextFormatting.GOLD + "Wildcard (*)"); + } + if (!lines.isEmpty()) { + drawHoveringText(lines, mouseX, mouseY, mc.fontRenderer); + } return; } } @@ -471,6 +635,7 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx } // Left grid click: toggle item selection and start drag + // Ctrl+Click stores a wildcard UID (matches all metadata variants) if (mouseButton == 0 && !filteredItems.isEmpty()) { int startIdx = leftPage * leftItemsPerPage; for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { @@ -480,9 +645,27 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx int y = leftGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { IIngredientListElement element = filteredItems.get(startIdx + i); - String uid = getIngredientUid(element.getIngredient()); + boolean ctrl = isCtrlKeyDown(); + String uid = ctrl + ? getIngredientWildcardUid(element.getIngredient()) + : getIngredientUid(element.getIngredient()); if (uid != null) { + // Normal click on an item already covered by a wildcard → deselect the wildcard + if (!ctrl && !selectedUids.contains(uid)) { + String wildcardUid = findCoveringWildcard(uid); + if (wildcardUid != null) { + removeSelectionByUid(wildcardUid); + return; + } + } + // Ctrl+Click adding a wildcard → remove any exact UIDs it already covers + if (ctrl && !selectedUids.contains(uid)) { + String prefix = uid.substring(0, uid.length() - 2); // strip ":*" + selectedUids.removeIf(existing -> + existing.equals(prefix) || existing.startsWith(prefix + ":")); + } dragAdding = !selectedUids.contains(uid); + dragWildcard = ctrl; isDragging = true; lastDraggedUid = uid; toggleSelectionByUid(uid); @@ -493,6 +676,7 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx } // Right grid click: remove from selection + // Uses selectedStackToStoredUid so wildcard entries are removed by their stored ":*" uid if (mouseButton == 0 && !selectedStacks.isEmpty()) { int startIdx = rightPage * rightItemsPerPage; for (int i = 0; i < rightItemsPerPage && (startIdx + i) < selectedStacks.size(); i++) { @@ -502,8 +686,8 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx int y = rightGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { IIngredientListElement element = selectedStacks.get(startIdx + i); - String uid = getIngredientUid(element.getIngredient()); - if (uid != null) removeSelectionByUid(uid); + String storedUid = selectedStackToStoredUid.get(element); + if (storedUid != null) removeSelectionByUid(storedUid); return; } } @@ -516,13 +700,15 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx protected void mouseReleased(int mouseX, int mouseY, int state) { super.mouseReleased(mouseX, mouseY, state); isDragging = false; + dragWildcard = false; lastDraggedUid = null; } @Override protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); - if (!isDragging || clickedMouseButton != 0 || filteredItems.isEmpty()) return; + // Wildcard selections are single-click only — no drag expansion + if (!isDragging || clickedMouseButton != 0 || filteredItems.isEmpty() || dragWildcard) return; int startIdx = leftPage * leftItemsPerPage; for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { int col = i % leftCols; @@ -534,7 +720,8 @@ protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, lo if (uid != null && !uid.equals(lastDraggedUid)) { lastDraggedUid = uid; if (dragAdding) { - if (selectedUids.add(uid)) updateSelectedStacks(); + // Skip items already covered by an exact or wildcard selection + if (!isUidSelected(uid) && selectedUids.add(uid)) updateSelectedStacks(); } else { if (selectedUids.remove(uid)) updateSelectedStacks(); } diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index 09d39ab94..23d29804a 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -103,7 +103,16 @@ public void loadCustomGroups() { if (group.id == null || group.id.isEmpty() || group.itemUids == null) { continue; } - Set uidSet = new HashSet<>(group.itemUids); + // Split stored UIDs into exact matches and wildcard prefixes (stored as "prefix:*") + Set exactUids = new HashSet<>(); + Set wildcardPrefixes = new HashSet<>(); + for (String uid : group.itemUids) { + if (uid.endsWith(":*")) { + wildcardPrefixes.add(uid.substring(0, uid.length() - 2)); + } else { + exactUids.add(uid); + } + } String displayName = group.displayName != null ? group.displayName : group.id; // Matcher works for both ItemStack and non-ItemStack ingredients (e.g. FluidStack): // for ItemStacks use StackHelper, for everything else use the generic IngredientRegistry helper. @@ -121,7 +130,12 @@ public void loadCustomGroups() { Internal.getIngredientRegistry().getIngredientHelper(ingredient); uid = helper.getUniqueId(ingredient); } - return uidSet.contains(uid); + if (exactUids.contains(uid)) return true; + // Check wildcard prefix: "minecraft:iron_pickaxe" matches "minecraft:iron_pickaxe:5" etc. + for (String prefix : wildcardPrefixes) { + if (uid.equals(prefix) || uid.startsWith(prefix + ":")) return true; + } + return false; } catch (Exception e) { return false; } From 271af0e53c0b285c48968efd0f2fb671c621ebf2 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 15 Mar 2026 05:04:04 -0400 Subject: [PATCH 26/48] Fix single collapsedstack Fixed single item collapsed stack to return item tooltip instead of group tooltip. --- src/main/java/mezz/jei/render/CollapsedStackRenderer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index 457d132e9..13cb3bd27 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -191,6 +191,12 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { List> ingredients = collapsedStack.getIngredients(); if (ingredients.isEmpty()) return; + // Single-item group (e.g. search filtered to one result): show the item's native tooltip + if (ingredients.size() == 1) { + new IngredientRenderer<>(ingredients.get(0)).drawTooltip(minecraft, mouseX, mouseY); + return; + } + FontRenderer font = minecraft.fontRenderer; final int COLS = 8; final int SLOT = 18; // 16px icon + 1px padding each side From c4949f7708ca319770012d2fa2f26ff33b900cc0 Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 17 Mar 2026 23:05:38 -0400 Subject: [PATCH 27/48] Item Selector Promote / Decompose Bring the item selection behavior on par with the blacklist. User selection is now less explicit, allowing promotions to wildcard and decompose of wildcards (all except). Configurations are saved similar to blacklist, promotes to wildcard if all meta space is selected and decomposed to explicit listings when not, regardless of selection method. --- .../collapsible/GuiCustomGroupEditor.java | 177 +++++++++++++++--- 1 file changed, 151 insertions(+), 26 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index b93c093aa..a96ed9d51 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -259,6 +259,59 @@ private String findCoveringWildcard(String normalUid) { return null; } + /** + * Collects all exact UIDs from the full ingredient list that share the given wildcard prefix. + * Used for auto-promote checks and wildcard decomposition. + * Mirrors the sibling-lookup from the legacy blacklist's areAllBlacklisted / getMatches logic. + */ + private List getSiblingUids(String wildcardUid) { + if (!wildcardUid.endsWith(":*") || !Internal.hasIngredientFilter()) return Collections.emptyList(); + String prefix = wildcardUid.substring(0, wildcardUid.length() - 2); + List all = Internal.getIngredientFilter().getIngredientList(""); + List result = new ArrayList<>(); + for (IIngredientListElement elem : all) { + String uid = getIngredientUid(elem.getIngredient()); + if (uid != null && (uid.equals(prefix) || uid.startsWith(prefix + ":"))) { + result.add(uid); + } + } + return result; + } + + /** + * Decomposes a wildcard entry into individual exact entries, excluding one item + * (the one the user just clicked to remove). + * Mirrors removeIngredientFromConfigBlacklist's wildcard-decompose behaviour. + */ + private void decomposeWildcard(String wildcardUid, @Nullable String excludeUid) { + selectedUids.remove(wildcardUid); + for (String sibling : getSiblingUids(wildcardUid)) { + if (!sibling.equals(excludeUid)) { + selectedUids.add(sibling); + } + } + updateSelectedStacks(); + } + + /** + * After adding an exact UID, checks if every meta variant of that item is now individually + * selected. If so, replaces them all with a single wildcard entry. + * Mirrors addIngredientToConfigBlacklist's auto-promote (areAllBlacklisted) behaviour. + */ + private void maybePromoteToWildcard(Object ingredient, String addedUid) { + String wildcardUid = getIngredientWildcardUid(ingredient); + if (wildcardUid == null || !wildcardUid.endsWith(":*")) return; + List siblings = getSiblingUids(wildcardUid); + if (siblings.isEmpty()) return; + for (String sibling : siblings) { + if (!selectedUids.contains(sibling)) return; + } + // All variants are individually selected — promote to a single wildcard entry + selectedUids.removeAll(siblings); + selectedUids.add(wildcardUid); + updateSelectedStacks(); + } + /** Returns a mutable list of tooltip lines for any ingredient element. */ @SuppressWarnings("unchecked") private List getIngredientTooltipLines(IIngredientListElement element) { @@ -576,7 +629,13 @@ private void drawLeftGridTooltips(int mouseX, int mouseY) { IIngredientListElement element = filteredItems.get(startIdx + i); List lines = getIngredientTooltipLines(element); if (element.getIngredient() instanceof ItemStack) { - lines.add(TextFormatting.GOLD + "Ctrl+Click: Wildcard (*)"); + String uid3 = getIngredientUid(element.getIngredient()); + boolean alreadySelected = uid3 != null && isUidSelected(uid3); + if (alreadySelected) { + lines.add(TextFormatting.GOLD + "Ctrl+Click: Remove all variants"); + } else { + lines.add(TextFormatting.GOLD + "Ctrl+Click: Select all variants (Wildcard)"); + } } String uid2 = getIngredientUid(element.getIngredient()); if (uid2 != null) { @@ -635,7 +694,8 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx } // Left grid click: toggle item selection and start drag - // Ctrl+Click stores a wildcard UID (matches all metadata variants) + // Ctrl+Click: adds a wildcard UID, or removes entire family if item is already selected + // Normal click: adds/removes exact UID, with auto-promote and wildcard-decompose support if (mouseButton == 0 && !filteredItems.isEmpty()) { int startIdx = leftPage * leftItemsPerPage; for (int i = 0; i < leftItemsPerPage && (startIdx + i) < filteredItems.size(); i++) { @@ -646,37 +706,76 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { IIngredientListElement element = filteredItems.get(startIdx + i); boolean ctrl = isCtrlKeyDown(); - String uid = ctrl - ? getIngredientWildcardUid(element.getIngredient()) - : getIngredientUid(element.getIngredient()); - if (uid != null) { - // Normal click on an item already covered by a wildcard → deselect the wildcard - if (!ctrl && !selectedUids.contains(uid)) { - String wildcardUid = findCoveringWildcard(uid); - if (wildcardUid != null) { - removeSelectionByUid(wildcardUid); - return; + String exactUid = getIngredientUid(element.getIngredient()); + if (exactUid == null) return; + + if (ctrl) { + if (isUidSelected(exactUid)) { + // Ctrl+Click on a selected item → remove the entire family (all meta variants) + String familyWildcard = getIngredientWildcardUid(element.getIngredient()); + if (familyWildcard != null && familyWildcard.endsWith(":*")) { + String prefix = familyWildcard.substring(0, familyWildcard.length() - 2); + selectedUids.remove(familyWildcard); + selectedUids.removeIf(existing -> + existing.equals(prefix) || existing.startsWith(prefix + ":")); + } else { + selectedUids.remove(exactUid); } + dragAdding = false; + dragWildcard = true; + isDragging = true; + lastDraggedUid = exactUid; + updateSelectedStacks(); + } else { + // Ctrl+Click on unselected item → add wildcard, remove any exact UIDs it already covers + String wildcardUid = getIngredientWildcardUid(element.getIngredient()); + if (wildcardUid == null) wildcardUid = exactUid; + if (wildcardUid.endsWith(":*")) { + String prefix = wildcardUid.substring(0, wildcardUid.length() - 2); + selectedUids.removeIf(existing -> + existing.equals(prefix) || existing.startsWith(prefix + ":")); + } + dragAdding = true; + dragWildcard = true; + isDragging = true; + lastDraggedUid = wildcardUid; + selectedUids.add(wildcardUid); + updateSelectedStacks(); } - // Ctrl+Click adding a wildcard → remove any exact UIDs it already covers - if (ctrl && !selectedUids.contains(uid)) { - String prefix = uid.substring(0, uid.length() - 2); // strip ":*" - selectedUids.removeIf(existing -> - existing.equals(prefix) || existing.startsWith(prefix + ":")); + } else { + // Normal click + if (!selectedUids.contains(exactUid)) { + // Item is covered by a wildcard → decompose to exact entries minus the clicked one + String coveringWildcard = findCoveringWildcard(exactUid); + if (coveringWildcard != null) { + decomposeWildcard(coveringWildcard, exactUid); + dragAdding = false; + dragWildcard = false; + isDragging = true; + lastDraggedUid = exactUid; + return; + } } - dragAdding = !selectedUids.contains(uid); - dragWildcard = ctrl; + boolean adding = !selectedUids.contains(exactUid); + dragAdding = adding; + dragWildcard = false; isDragging = true; - lastDraggedUid = uid; - toggleSelectionByUid(uid); + lastDraggedUid = exactUid; + toggleSelectionByUid(exactUid); + // Auto-promote: if all meta variants are now individually selected, consolidate to a wildcard + if (adding) { + maybePromoteToWildcard(element.getIngredient(), exactUid); + } } return; } } } - // Right grid click: remove from selection - // Uses selectedStackToStoredUid so wildcard entries are removed by their stored ":*" uid + // Right grid click: remove from selection. + // Ctrl+Click: wildcard removal — removes the entire family (wildcard entry + all exact siblings). + // Normal click on a wildcard entry: decomposes it, removing only the clicked item. + // Normal click on an exact entry: removes just that item. if (mouseButton == 0 && !selectedStacks.isEmpty()) { int startIdx = rightPage * rightItemsPerPage; for (int i = 0; i < rightItemsPerPage && (startIdx + i) < selectedStacks.size(); i++) { @@ -687,7 +786,28 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { IIngredientListElement element = selectedStacks.get(startIdx + i); String storedUid = selectedStackToStoredUid.get(element); - if (storedUid != null) removeSelectionByUid(storedUid); + if (storedUid != null) { + boolean ctrl = isCtrlKeyDown(); + if (ctrl) { + // Ctrl+Click — remove all variants regardless of exact/wildcard storage + String familyWildcard = getIngredientWildcardUid(element.getIngredient()); + if (familyWildcard != null && familyWildcard.endsWith(":*")) { + String prefix = familyWildcard.substring(0, familyWildcard.length() - 2); + selectedUids.remove(familyWildcard); + selectedUids.removeIf(existing -> + existing.equals(prefix) || existing.startsWith(prefix + ":")); + updateSelectedStacks(); + } else { + removeSelectionByUid(storedUid); + } + } else if (storedUid.endsWith(":*")) { + // Normal click on a wildcard entry — decompose, removing only this item + String exactUid = getIngredientUid(element.getIngredient()); + decomposeWildcard(storedUid, exactUid); + } else { + removeSelectionByUid(storedUid); + } + } return; } } @@ -716,12 +836,17 @@ protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, lo int x = leftGridX + col * ITEM_SIZE; int y = leftGridY + row * ITEM_SIZE; if (mouseX >= x && mouseX < x + ITEM_SIZE && mouseY >= y && mouseY < y + ITEM_SIZE) { - String uid = getIngredientUid(filteredItems.get(startIdx + i).getIngredient()); + IIngredientListElement elem = filteredItems.get(startIdx + i); + String uid = getIngredientUid(elem.getIngredient()); if (uid != null && !uid.equals(lastDraggedUid)) { lastDraggedUid = uid; if (dragAdding) { // Skip items already covered by an exact or wildcard selection - if (!isUidSelected(uid) && selectedUids.add(uid)) updateSelectedStacks(); + if (!isUidSelected(uid) && selectedUids.add(uid)) { + updateSelectedStacks(); + // Auto-promote: if all variants of this item are selected, consolidate to a wildcard + maybePromoteToWildcard(elem.getIngredient(), uid); + } } else { if (selectedUids.remove(uid)) updateSelectedStacks(); } From f036d804401ff62d59a79af0cbbee1b593455a13 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 18 Mar 2026 05:20:58 -0400 Subject: [PATCH 28/48] Fix Alignments and thin buttons Fixed alignments of item grid on gui selector. Fixed several buttons a few pixels too thin. --- .../collapsible/GuiCollapsibleGroups.java | 6 +++--- .../collapsible/GuiCustomGroupEditor.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index df2d7338d..7b6ff6532 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -149,14 +149,14 @@ private void rebuildPageButtons() { String toggleLabel = card.enabled ? Translator.translateToLocal("jei.gui.collapsible.enabled") : Translator.translateToLocal("jei.gui.collapsible.disabled"); - this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 16, toggleLabel)); + this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 20, toggleLabel)); if (card.isCustom) { // Configure button - this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 18, 24, 16, + this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 22, 24, 20, "\u270E")); // pencil unicode // Delete button - this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 18, 26, 16, + this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 22, 26, 20, "\u2716")); // cross unicode } } diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index a96ed9d51..365242cac 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -104,7 +104,7 @@ public void initGui() { int panelDivider = (int) (this.width * 0.65); // Name field - nameField = new GuiTextField(10, this.fontRenderer, 60, 6, panelDivider - 70, 16); + nameField = new GuiTextField(10, this.fontRenderer, 62, 6, panelDivider - 70, 16); nameField.setMaxStringLength(40); nameField.setText(group.displayName != null ? group.displayName : ""); @@ -114,9 +114,9 @@ public void initGui() { searchField.setText(""); // Save & Cancel buttons - this.buttonList.add(new GuiButton(BTN_SAVE, panelDivider + 4, 4, 50, 18, + this.buttonList.add(new GuiButton(BTN_SAVE, panelDivider + 4, 4, 50, 20, Translator.translateToLocal("jei.gui.collapsible.editor.save"))); - this.buttonList.add(new GuiButton(BTN_CANCEL, panelDivider + 58, 4, 50, 18, + this.buttonList.add(new GuiButton(BTN_CANCEL, panelDivider + 58, 4, 50, 20, Translator.translateToLocal("jei.gui.collapsible.back"))); // Calculate left grid layout @@ -124,7 +124,7 @@ public void initGui() { int leftHeight = this.height - topBarHeight - 26; // room for page nav leftCols = Math.max(1, leftWidth / ITEM_SIZE); leftRows = Math.max(1, leftHeight / ITEM_SIZE); - leftGridX = 4; + leftGridX = (panelDivider - 4 - leftCols * ITEM_SIZE) / 2; leftGridY = topBarHeight; leftItemsPerPage = leftCols * leftRows; @@ -139,12 +139,12 @@ public void initGui() { // Page nav buttons for left grid int leftNavY = this.height - 22; - this.buttonList.add(new GuiButton(BTN_PREV_PAGE, 4, leftNavY, 30, 18, "<")); - this.buttonList.add(new GuiButton(BTN_NEXT_PAGE, panelDivider - 34, leftNavY, 30, 18, ">")); + this.buttonList.add(new GuiButton(BTN_PREV_PAGE, 4, leftNavY, 30, 20, "<")); + this.buttonList.add(new GuiButton(BTN_NEXT_PAGE, panelDivider - 34, leftNavY, 30, 20, ">")); // Page nav buttons for right grid - this.buttonList.add(new GuiButton(BTN_PREV_SEL_PAGE, panelDivider + 4, leftNavY, 30, 18, "<")); - this.buttonList.add(new GuiButton(BTN_NEXT_SEL_PAGE, this.width - 34, leftNavY, 30, 18, ">")); + this.buttonList.add(new GuiButton(BTN_PREV_SEL_PAGE, panelDivider + 4, leftNavY, 30, 20, "<")); + this.buttonList.add(new GuiButton(BTN_NEXT_SEL_PAGE, this.width - 34, leftNavY, 30, 20, ">")); updateFilteredItems(); updateSelectedStacks(); From 89e87a1ea996dbb8154bb497146c6da237cfeb0f Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 22 Mar 2026 02:23:33 -0400 Subject: [PATCH 29/48] Fixed client lag spikes on collapse/expand. Collapsed and expand invalidated the filter cache causing client lag spikes. --- src/main/java/mezz/jei/ingredients/IngredientFilter.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index 4d1ba8fbd..c09ff9ff5 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -434,12 +434,8 @@ public void addCollapsedStateListener(Runnable listener) { collapsedStateListeners.add(listener); } - /** - * Called when a group is expanded or collapsed. Invalidates the cached collapsed list - * and notifies only collapsed-state listeners (preserves the current page position). - */ public void notifyCollapsedStateChanged() { - this.filterCached = null; + // Do NOT null filterCached here. Creates client lag spikes. for (Runnable listener : collapsedStateListeners) { listener.run(); } From b3156a2049b2b848b967231db76252eefc2b332c Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 22 Mar 2026 02:56:27 -0400 Subject: [PATCH 30/48] Improved search latency --- .../mezz/jei/ingredients/CollapsedStack.java | 46 +++++++++++ .../ingredients/CollapsedStackRegistry.java | 45 +++++------ .../jei/ingredients/IngredientFilter.java | 79 +++++++++++++++---- 3 files changed, 129 insertions(+), 41 deletions(-) diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStack.java b/src/main/java/mezz/jei/ingredients/CollapsedStack.java index 26e22db5e..67fae0600 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStack.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStack.java @@ -1,9 +1,12 @@ package mezz.jei.ingredients; +import mezz.jei.Internal; +import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.api.recipe.IIngredientType; import mezz.jei.gui.ingredients.IIngredientListElement; import net.minecraft.item.ItemStack; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; @@ -25,6 +28,13 @@ public class CollapsedStack { private final String displayName; /** Matches against the raw ingredient object (any type). */ private final Predicate matcher; + /** + * Optional fast-path matcher that receives a pre-computed UID string instead of the raw + * ingredient. Set on custom groups by {@link CollapsedStackRegistry} so that + * {@link mezz.jei.ingredients.IngredientFilter#collapse} can skip calling + * {@code getUniqueIdentifierForStack()} N×M times per filter cycle. + */ + @Nullable private Predicate uidMatcher; private boolean expanded; private final List> ingredients; @@ -83,6 +93,42 @@ public boolean matches(IIngredientListElement element) { return matcher.test(ingredient); } + /** Returns the UID-based matcher, or {@code null} if this group uses a raw-ingredient predicate. */ + @Nullable + public Predicate getUidMatcher() { + return uidMatcher; + } + + public void setUidMatcher(Predicate uidMatcher) { + this.uidMatcher = uidMatcher; + } + + /** + * Computes a unique identifier string for any ingredient type. + * Returns {@code null} if the ingredient is empty or an error occurs. + * Used by {@code collapse()} to precompute UIDs once per element. + */ + @Nullable + public static String computeIngredientUid(Object ingredient) { + if (ingredient instanceof ItemStack) { + ItemStack stack = (ItemStack) ingredient; + if (stack.isEmpty()) return null; + try { + return Internal.getStackHelper().getUniqueIdentifierForStack(stack); + } catch (Exception e) { + return null; + } + } + try { + @SuppressWarnings("unchecked") + IIngredientHelper helper = (IIngredientHelper) + Internal.getIngredientRegistry().getIngredientHelper(ingredient); + return helper.getUniqueId(ingredient); + } catch (Exception e) { + return null; + } + } + // --- Runtime ingredient list (transient per filter cycle) --- public List> getIngredients() { diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index 23d29804a..de2586393 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -1,6 +1,5 @@ package mezz.jei.ingredients; -import mezz.jei.Internal; import mezz.jei.config.Config; import mezz.jei.config.CustomGroupsConfig; import mezz.jei.gui.ingredients.IIngredientListElement; @@ -114,32 +113,26 @@ public void loadCustomGroups() { } } String displayName = group.displayName != null ? group.displayName : group.id; - // Matcher works for both ItemStack and non-ItemStack ingredients (e.g. FluidStack): - // for ItemStacks use StackHelper, for everything else use the generic IngredientRegistry helper. - customEntries.add(new CollapsedStack(group.id, displayName, ingredient -> { - try { - String uid; - if (ingredient instanceof ItemStack) { - ItemStack stack = (ItemStack) ingredient; - if (stack.isEmpty()) return false; - uid = Internal.getStackHelper().getUniqueIdentifierForStack(stack); - } else { - @SuppressWarnings("unchecked") - mezz.jei.api.ingredients.IIngredientHelper helper = - (mezz.jei.api.ingredients.IIngredientHelper) - Internal.getIngredientRegistry().getIngredientHelper(ingredient); - uid = helper.getUniqueId(ingredient); - } - if (exactUids.contains(uid)) return true; - // Check wildcard prefix: "minecraft:iron_pickaxe" matches "minecraft:iron_pickaxe:5" etc. - for (String prefix : wildcardPrefixes) { - if (uid.equals(prefix) || uid.startsWith(prefix + ":")) return true; - } - return false; - } catch (Exception e) { - return false; + + // UID-based fast-path predicate: O(1) hash-set lookup, no StackHelper call. + // Used by IngredientFilter.collapse() after it has pre-computed each element's UID once. + final Predicate uidPredicate = uid -> { + if (exactUids.contains(uid)) return true; + for (String prefix : wildcardPrefixes) { + if (uid.equals(prefix) || uid.startsWith(prefix + ":")) return true; } - })); + return false; + }; + + // Ingredient-level matcher (fallback for call sites that don't pre-compute UIDs, + // e.g. withGroupNameMatches). Delegates UID computation to CollapsedStack.computeIngredientUid + // and then uses the same uidPredicate to avoid duplicating the matching logic. + CollapsedStack cs = new CollapsedStack(group.id, displayName, ingredient -> { + String uid = CollapsedStack.computeIngredientUid(ingredient); + return uid != null && uidPredicate.test(uid); + }); + cs.setUidMatcher(uidPredicate); + customEntries.add(cs); } Log.get().debug("Loaded {} custom collapsible groups", customEntries.size()); } diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index c09ff9ff5..b381a67f1 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -3,6 +3,7 @@ import javax.annotation.Nullable; import java.util.*; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -38,6 +39,15 @@ public class IngredientFilter implements IIngredientFilter, IIngredientGridSourc private List ingredientListCached = Collections.emptyList(); private List collapsedListCached = Collections.emptyList(); @Nullable private String filterCached; + /** + * Cached sorted list of all currently-visible ingredients — the result of a full + * suffix-tree traversal + sort. This does NOT change when the search-bar text changes, + * only when ingredients are added/removed or their visibility changes. Caching it here + * avoids the expensive {@code elementSearch.getAllIngredients()} traversal on every + * keystroke (once in {@link #getIngredientListUncached} for empty filter and once more + * in {@link #withGroupNameMatches} for every non-empty filter). + */ + @Nullable private List> allVisibleIngredientsCache = null; private boolean afterBlock = false; @Nullable private List delegatedActions; @@ -56,13 +66,13 @@ public void logStatistics() { public void addIngredients(NonNullList ingredients) { ingredients.sort(IngredientListElementComparator.INSTANCE); this.elementSearch.addAll(ingredients); - this.filterCached = null; + invalidateCache(); } public void addIngredient(IIngredientListElement element) { updateHiddenState(element); this.elementSearch.add(element); - this.filterCached = null; + invalidateCache(); } public void delegateAfterBlock(Runnable runnable) { @@ -97,6 +107,23 @@ public void block() { public void invalidateCache() { this.filterCached = null; + this.allVisibleIngredientsCache = null; + } + + /** + * Returns a cached, sorted list of every currently-visible ingredient. + * The cache is invalidated whenever {@link #invalidateCache()} is called (ingredient + * additions, visibility changes, mode changes), but NOT on search-text changes — the + * full ingredient set is independent of the search bar content. + */ + private List> getAllVisibleIngredients() { + if (allVisibleIngredientsCache == null) { + allVisibleIngredientsCache = this.elementSearch.getAllIngredients().stream() + .filter(IIngredientListElement::isVisible) + .sorted(IngredientListElementComparator.INSTANCE) + .collect(Collectors.toList()); + } + return allVisibleIngredientsCache; } public List> findMatchingElements(IIngredientListElement element) { @@ -140,7 +167,7 @@ public void modesChanged() { @SubscribeEvent public void onEditModeToggleEvent(EditModeToggleEvent event) { - this.filterCached = null; + invalidateCache(); updateHidden(); // In Hide Ingredients Mode the user cannot Alt+Click to expand/collapse groups, @@ -171,7 +198,7 @@ public void updateHiddenState(IIngredientListElement element) { (Config.isEditModeEnabled() || !Config.isIngredientOnConfigBlacklist(ingredient, ingredientHelper)); if (element.isVisible() != visible) { element.setVisible(visible); - this.filterCached = null; + invalidateCache(); } } @@ -245,20 +272,14 @@ public void setFilterText(String filterText) { private List> getIngredientListUncached(String filterText) { if (filterText.isEmpty()) { - return this.elementSearch.getAllIngredients().stream() - .filter(IIngredientListElement::isVisible) - .sorted(IngredientListElementComparator.INSTANCE) - .collect(Collectors.toList()); + return new ArrayList<>(getAllVisibleIngredients()); } List tokens = Arrays.stream(filterText.split("\\|")) .map(SearchToken::parseSearchToken) .filter(s -> !s.search.isEmpty()) .collect(Collectors.toList()); if (tokens.isEmpty()) { - return this.elementSearch.getAllIngredients().stream() - .filter(IIngredientListElement::isVisible) - .sorted(IngredientListElementComparator.INSTANCE) - .collect(Collectors.toList()); + return new ArrayList<>(getAllVisibleIngredients()); } return tokens.stream() .map(token -> token.getSearchResults(this.elementSearch)) @@ -295,8 +316,9 @@ private List> withGroupNameMatches( Set> seen = Collections.newSetFromMap(new IdentityHashMap<>()); seen.addAll(baseList); List> result = new ArrayList<>(baseList); - for (IIngredientListElement element : this.elementSearch.getAllIngredients()) { - if (!element.isVisible() || seen.contains(element)) { + // getAllVisibleIngredients() is cached — no extra suffix-tree traversal per keystroke. + for (IIngredientListElement element : getAllVisibleIngredients()) { + if (seen.contains(element)) { continue; } for (CollapsedStack entry : matchingGroups) { @@ -353,10 +375,36 @@ private List collapse(List ingredientList) { // Track which entries have already been added to the result list Set addedToResult = Collections.newSetFromMap(new IdentityHashMap<>()); + // Precompute per-element UIDs if any active entry has a UID-based matcher. + // This reduces getUniqueIdentifierForStack() calls from O(n×m) to O(n) for + // custom groups (which are the expensive ones in the profiler). + boolean hasUidEntries = false; + for (CollapsedStack entry : activeEntries) { + if (entry.getUidMatcher() != null) { + hasUidEntries = true; + break; + } + } + final String[] elementUids; + if (hasUidEntries) { + elementUids = new String[ingredientList.size()]; + for (int i = 0; i < ingredientList.size(); i++) { + elementUids[i] = CollapsedStack.computeIngredientUid(ingredientList.get(i).getIngredient()); + } + } else { + elementUids = null; + } + + int idx = 0; for (IIngredientListElement element : ingredientList) { + final String cachedUid = elementUids != null ? elementUids[idx] : null; boolean matched = false; for (CollapsedStack entry : activeEntries) { - if (entry.matches(element)) { + Predicate uidMatcher = entry.getUidMatcher(); + boolean isMatch = (uidMatcher != null && cachedUid != null) + ? uidMatcher.test(cachedUid) + : entry.matches(element); + if (isMatch) { if (addedToResult.add(entry)) { result.add(entry); } @@ -367,6 +415,7 @@ private List collapse(List ingredientList) { if (!matched) { result.add(element); } + idx++; } // Remove empty collapsed stacks (shouldn't happen, but be safe) From 0800495b10e2c57f1db6f35febbcba1e190b3224 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 22 Mar 2026 05:25:52 -0400 Subject: [PATCH 31/48] Item selector QoL and delete prompts. Implements a basic delete confirm prompt so users don't delete groups in a single click. Item Selector now remembers last search term and page, very useful for quickly and repeatedly adding groups. Ctrl+F now focuses the search field. --- .gitignore | 1 + TODO.md | 1 + .../collapsible/GuiCollapsibleGroups.java | 57 ++++++++++++++++--- .../collapsible/GuiCustomGroupEditor.java | 30 ++++++++-- src/main/resources/assets/jei/lang/en_us.lang | 1 + 5 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 TODO.md diff --git a/.gitignore b/.gitignore index 872402574..ff4a1f1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ changelog.html \.project \.settings/ *.launch +/Reference diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..464090415 --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +# TODO diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 7b6ff6532..0e261953c 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -53,11 +53,15 @@ public class GuiCollapsibleGroups extends GuiScreen { private static final int BTN_TOGGLE_BASE = 100; private static final int BTN_CONFIGURE_BASE = 200; private static final int BTN_DELETE_BASE = 300; + private static final int BTN_DELETE_CONFIRM_BASE = 400; + private static final int BTN_DELETE_CANCEL_BASE = 500; private final GuiScreen parentScreen; private final List cardEntries = new ArrayList<>(); private int currentPage = 0; private int totalPages = 1; + /** Index into {@code cardEntries} of the custom group awaiting delete confirmation, or -1. */ + private int pendingDeleteIdx = -1; @Nullable private IIngredientListElement tooltipElement = null; // Drag-to-scroll state for card preview boxes @@ -152,12 +156,20 @@ private void rebuildPageButtons() { this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 20, toggleLabel)); if (card.isCustom) { - // Configure button - this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 22, 24, 20, - "\u270E")); // pencil unicode - // Delete button - this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 22, 26, 20, - "\u2716")); // cross unicode + if (i == pendingDeleteIdx) { + // Confirm row: ✔ Yes / ✗ No + this.buttonList.add(new GuiButton(BTN_DELETE_CONFIRM_BASE + i, btnX, btnY + 22, 24, 20, + "\u2714")); // ✔ checkmark + this.buttonList.add(new GuiButton(BTN_DELETE_CANCEL_BASE + i, btnX + 26, btnY + 22, 26, 20, + "\u2716")); // ✗ cancel + } else { + // Configure button + this.buttonList.add(new GuiButton(BTN_CONFIGURE_BASE + i, btnX, btnY + 22, 24, 20, + "\u270E")); // pencil unicode + // Delete button + this.buttonList.add(new GuiButton(BTN_DELETE_BASE + i, btnX + 26, btnY + 22, 26, 20, + "\u2716")); // cross unicode + } } } @@ -186,11 +198,13 @@ protected void actionPerformed(GuiButton button) throws IOException { } if (button.id == BTN_PREV_PAGE) { currentPage = Math.max(0, currentPage - 1); + pendingDeleteIdx = -1; rebuildPageButtons(); return; } if (button.id == BTN_NEXT_PAGE) { currentPage = Math.min(totalPages - 1, currentPage + 1); + pendingDeleteIdx = -1; rebuildPageButtons(); return; } @@ -244,9 +258,20 @@ protected void actionPerformed(GuiButton button) throws IOException { return; } - // Delete - if (button.id >= BTN_DELETE_BASE) { + // Delete — first click: arm confirmation + if (button.id >= BTN_DELETE_BASE && button.id < BTN_DELETE_CONFIRM_BASE) { int idx = button.id - BTN_DELETE_BASE; + if (idx >= 0 && idx < cardEntries.size() && cardEntries.get(idx).isCustom) { + pendingDeleteIdx = idx; + rebuildPageButtons(); + } + return; + } + + // Delete confirmed — execute the actual removal + if (button.id >= BTN_DELETE_CONFIRM_BASE && button.id < BTN_DELETE_CANCEL_BASE) { + int idx = button.id - BTN_DELETE_CONFIRM_BASE; + pendingDeleteIdx = -1; if (idx >= 0 && idx < cardEntries.size()) { GroupCardEntry card = cardEntries.get(idx); if (card.isCustom) { @@ -267,6 +292,13 @@ protected void actionPerformed(GuiButton button) throws IOException { } } } + return; + } + + // Delete cancelled + if (button.id >= BTN_DELETE_CANCEL_BASE) { + pendingDeleteIdx = -1; + rebuildPageButtons(); } } @@ -290,6 +322,15 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { int cardY = layoutContentTop + row * (CARD_HEIGHT + CARD_PADDING); GroupCardEntry card = cardEntries.get(i); drawCard(card, cardX, cardY, layoutColWidth, mouseX, mouseY); + + // Draw "Delete?" label to the left of the ✔/✗ confirm buttons + if (i == pendingDeleteIdx && card.isCustom) { + int btnX = cardX + layoutColWidth - 56; + int btnY = cardY + 4; + String confirmLabel = Translator.translateToLocal("jei.gui.collapsible.confirmDelete"); + int labelX = btnX - this.fontRenderer.getStringWidth(confirmLabel) - 3; + this.fontRenderer.drawStringWithShadow(confirmLabel, labelX, btnY + 27, 0xFFFF4444); + } } // Page counter diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index 365242cac..dfdf28178 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -46,6 +46,10 @@ public class GuiCustomGroupEditor extends GuiScreen { private final CustomGroupsConfig.CustomGroup group; private final Set selectedUids = new LinkedHashSet<>(); + // Persisted across editor instances, reopening restores last search and page. + private static String savedSearchText = ""; + private static int savedFirstItemIndex = 0; + @Nullable private GuiTextField nameField; @Nullable @@ -108,10 +112,10 @@ public void initGui() { nameField.setMaxStringLength(40); nameField.setText(group.displayName != null ? group.displayName : ""); - // Search field + // Search field — restore saved text so the user's last search carries over searchField = new GuiTextField(11, this.fontRenderer, 4, topBarHeight - 18, panelDivider - 12, 14); searchField.setMaxStringLength(128); - searchField.setText(""); + searchField.setText(savedSearchText); // Save & Cancel buttons this.buttonList.add(new GuiButton(BTN_SAVE, panelDivider + 4, 4, 50, 20, @@ -147,6 +151,7 @@ public void initGui() { this.buttonList.add(new GuiButton(BTN_NEXT_SEL_PAGE, this.width - 34, leftNavY, 30, 20, ">")); updateFilteredItems(); + leftPage = Math.max(0, Math.min(savedFirstItemIndex / leftItemsPerPage, leftTotalPages - 1)); updateSelectedStacks(); buildOtherGroupIndex(); } @@ -155,6 +160,9 @@ public void initGui() { public void onGuiClosed() { super.onGuiClosed(); Keyboard.enableRepeatEvents(false); + // Persist the current search text and the absolute index of the first visible item. + savedSearchText = (searchField != null) ? searchField.getText() : ""; + savedFirstItemIndex = leftPage * leftItemsPerPage; } /** @@ -262,7 +270,6 @@ private String findCoveringWildcard(String normalUid) { /** * Collects all exact UIDs from the full ingredient list that share the given wildcard prefix. * Used for auto-promote checks and wildcard decomposition. - * Mirrors the sibling-lookup from the legacy blacklist's areAllBlacklisted / getMatches logic. */ private List getSiblingUids(String wildcardUid) { if (!wildcardUid.endsWith(":*") || !Internal.hasIngredientFilter()) return Collections.emptyList(); @@ -281,7 +288,6 @@ private List getSiblingUids(String wildcardUid) { /** * Decomposes a wildcard entry into individual exact entries, excluding one item * (the one the user just clicked to remove). - * Mirrors removeIngredientFromConfigBlacklist's wildcard-decompose behaviour. */ private void decomposeWildcard(String wildcardUid, @Nullable String excludeUid) { selectedUids.remove(wildcardUid); @@ -296,7 +302,6 @@ private void decomposeWildcard(String wildcardUid, @Nullable String excludeUid) /** * After adding an exact UID, checks if every meta variant of that item is now individually * selected. If so, replaces them all with a single wildcard entry. - * Mirrors addIngredientToConfigBlacklist's auto-promote (areAllBlacklisted) behaviour. */ private void maybePromoteToWildcard(Object ingredient, String addedUid) { String wildcardUid = getIngredientWildcardUid(ingredient); @@ -689,6 +694,7 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx // Right-click clears search if (searchField.isFocused() && mouseButton == 1) { searchField.setText(""); + leftPage = 0; updateFilteredItems(); } } @@ -877,10 +883,24 @@ protected void keyTyped(char typedChar, int keyCode) throws IOException { return; } if (searchField != null && searchField.isFocused()) { + String before = searchField.getText(); searchField.textboxKeyTyped(typedChar, keyCode); + if (!searchField.getText().equals(before)) { + leftPage = 0; + } updateFilteredItems(); return; } + // Ctrl+F focuses the search field + if (keyCode == Keyboard.KEY_F && isCtrlKeyDown()) { + if (searchField != null) { + searchField.setFocused(true); + if (nameField != null) { + nameField.setFocused(false); + } + } + return; + } if (keyCode == Keyboard.KEY_ESCAPE) { this.mc.displayGuiScreen(parentScreen); return; diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index cf2781917..b8b58b2c1 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -233,4 +233,5 @@ jei.gui.collapsible.editor.title=Selected Items jei.gui.collapsible.editor.name=Name jei.gui.collapsible.editor.save=Save jei.gui.collapsible.editor.selected=%d selected +jei.gui.collapsible.confirmDelete=Delete? hei.tooltip.missing_ingredients=§cMissing Ingredients: \ No newline at end of file From 57d486f79aefe8b98183e835ecf49f99c39e4187 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 22 Mar 2026 05:38:03 -0400 Subject: [PATCH 32/48] Bump Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f8fbabf35..ae8cbe941 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ generate_javadocs_jar = false # Mod Information # HIGHLY RECOMMEND complying with SemVer for mod_version: https://semver.org/ -mod_version = 4.29.15 +mod_version = 4.30.2 root_package = mezz mod_id = jei mod_name = Had Enough Items From 5b8688bb057cb86bd4c7b8853c4b5435048c4402 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 22 Mar 2026 06:11:12 -0400 Subject: [PATCH 33/48] Group membership cache ... magic. --- .gitignore | 1 + gradle.properties | 2 +- .../ingredients/CollapsedStackRegistry.java | 14 +- .../jei/ingredients/IngredientFilter.java | 129 ++++++++++++------ 4 files changed, 102 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index ff4a1f1b5..b9304af82 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ changelog.html \.settings/ *.launch /Reference +/.vscode diff --git a/gradle.properties b/gradle.properties index ae8cbe941..8bd31a27e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ generate_javadocs_jar = false # Mod Information # HIGHLY RECOMMEND complying with SemVer for mod_version: https://semver.org/ -mod_version = 4.30.2 +mod_version = 4.30.3 root_package = mezz mod_id = jei mod_name = Had Enough Items diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index de2586393..ec16abfcb 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -114,12 +114,20 @@ public void loadCustomGroups() { } String displayName = group.displayName != null ? group.displayName : group.id; - // UID-based fast-path predicate: O(1) hash-set lookup, no StackHelper call. + // UID-based fast-path predicate: O(K) hash-set lookups (K = number of ':' segments + // in the UID, typically 2–3) instead of O(W) iteration over all wildcard prefixes. // Used by IngredientFilter.collapse() after it has pre-computed each element's UID once. final Predicate uidPredicate = uid -> { if (exactUids.contains(uid)) return true; - for (String prefix : wildcardPrefixes) { - if (uid.equals(prefix) || uid.startsWith(prefix + ":")) return true; + if (!wildcardPrefixes.isEmpty()) { + // Check if the UID itself is a wildcard prefix (uid.equals(prefix)) + if (wildcardPrefixes.contains(uid)) return true; + // Walk colon boundaries and check each prefix substring against the set + int idx = 0; + while ((idx = uid.indexOf(':', idx)) >= 0) { + if (wildcardPrefixes.contains(uid.substring(0, idx))) return true; + idx++; + } } return false; }; diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index b381a67f1..5e3ef80fe 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -48,6 +48,14 @@ public class IngredientFilter implements IIngredientFilter, IIngredientGridSourc * in {@link #withGroupNameMatches} for every non-empty filter). */ @Nullable private List> allVisibleIngredientsCache = null; + /** + * Precomputed mapping from each visible element (by identity) to the list of + * {@link CollapsedStack} groups it belongs to. Built once from + * {@link #getAllVisibleIngredients()} and ALL registered groups; reused across + * keystrokes. Invalidated alongside {@code allVisibleIngredientsCache} when + * ingredients or groups change. + */ + @Nullable private IdentityHashMap, List> groupMembershipCache = null; private boolean afterBlock = false; @Nullable private List delegatedActions; @@ -108,6 +116,7 @@ public void block() { public void invalidateCache() { this.filterCached = null; this.allVisibleIngredientsCache = null; + this.groupMembershipCache = null; } /** @@ -126,6 +135,56 @@ private List> getAllVisibleIngredients() { return allVisibleIngredientsCache; } + /** + * Returns a cached identity-map from each visible element to the list of + * {@link CollapsedStack} groups it matches. Elements with no group match are + * absent from the map. The cache is built once from the full visible-ingredient + * list and ALL registered groups (both built-in and custom), so the expensive + * matcher / UID computation happens only once — not on every keystroke. + *

+ * {@link #collapse} then filters this by the currently-active (enabled) entries + * and uses O(1) map lookups per element instead of re-running matchers. + */ + private IdentityHashMap, List> getGroupMembership() { + if (groupMembershipCache == null) { + groupMembershipCache = new IdentityHashMap<>(); + if (!Config.isCollapsibleGroupsEnabled()) return groupMembershipCache; + + CollapsedStackRegistry registry = CollapsedStackRegistry.getInstance(); + List allEntries = new ArrayList<>(); + allEntries.addAll(registry.getEntries()); + allEntries.addAll(registry.getCustomEntries()); + if (allEntries.isEmpty()) return groupMembershipCache; + + boolean hasUidEntries = false; + for (CollapsedStack entry : allEntries) { + if (entry.getUidMatcher() != null) { + hasUidEntries = true; + break; + } + } + + for (IIngredientListElement element : getAllVisibleIngredients()) { + String uid = hasUidEntries ? CollapsedStack.computeIngredientUid(element.getIngredient()) : null; + List matched = null; + for (CollapsedStack entry : allEntries) { + Predicate uidMatcher = entry.getUidMatcher(); + boolean isMatch = (uidMatcher != null && uid != null) + ? uidMatcher.test(uid) + : entry.matches(element); + if (isMatch) { + if (matched == null) matched = new ArrayList<>(2); + matched.add(entry); + } + } + if (matched != null) { + groupMembershipCache.put(element, matched); + } + } + } + return groupMembershipCache; + } + public List> findMatchingElements(IIngredientListElement element) { final IIngredientHelper ingredientHelper = element.getIngredientHelper(); final V ingredient = element.getIngredient(); @@ -315,17 +374,23 @@ private List> withGroupNameMatches( // Use identity comparison so dedup works regardless of equals() implementation. Set> seen = Collections.newSetFromMap(new IdentityHashMap<>()); seen.addAll(baseList); + Set matchingGroupSet = Collections.newSetFromMap(new IdentityHashMap<>()); + matchingGroupSet.addAll(matchingGroups); List> result = new ArrayList<>(baseList); - // getAllVisibleIngredients() is cached — no extra suffix-tree traversal per keystroke. + // Use the precomputed membership cache instead of re-running matchers. + IdentityHashMap, List> membership = getGroupMembership(); for (IIngredientListElement element : getAllVisibleIngredients()) { if (seen.contains(element)) { continue; } - for (CollapsedStack entry : matchingGroups) { - if (entry.matches(element)) { - result.add(element); - seen.add(element); - break; + List groups = membership.get(element); + if (groups != null) { + for (CollapsedStack entry : groups) { + if (matchingGroupSet.contains(entry)) { + result.add(element); + seen.add(element); + break; + } } } } @@ -371,51 +436,35 @@ private List collapse(List ingredientList) { for (CollapsedStack entry : activeEntries) { entry.clearIngredients(); } + + // Use the precomputed group-membership cache for O(1) per-element lookups. + // The cache maps each visible element to ALL groups it belongs to (built once), + // so we only need to intersect with the active set here — no matchers, no UID + // computation on the per-keystroke path. + Set activeSet = Collections.newSetFromMap(new IdentityHashMap<>()); + activeSet.addAll(activeEntries); + IdentityHashMap, List> membership = getGroupMembership(); + List result = new ArrayList<>(ingredientList.size()); - // Track which entries have already been added to the result list Set addedToResult = Collections.newSetFromMap(new IdentityHashMap<>()); - // Precompute per-element UIDs if any active entry has a UID-based matcher. - // This reduces getUniqueIdentifierForStack() calls from O(n×m) to O(n) for - // custom groups (which are the expensive ones in the profiler). - boolean hasUidEntries = false; - for (CollapsedStack entry : activeEntries) { - if (entry.getUidMatcher() != null) { - hasUidEntries = true; - break; - } - } - final String[] elementUids; - if (hasUidEntries) { - elementUids = new String[ingredientList.size()]; - for (int i = 0; i < ingredientList.size(); i++) { - elementUids[i] = CollapsedStack.computeIngredientUid(ingredientList.get(i).getIngredient()); - } - } else { - elementUids = null; - } - - int idx = 0; for (IIngredientListElement element : ingredientList) { - final String cachedUid = elementUids != null ? elementUids[idx] : null; + List groups = membership.get(element); boolean matched = false; - for (CollapsedStack entry : activeEntries) { - Predicate uidMatcher = entry.getUidMatcher(); - boolean isMatch = (uidMatcher != null && cachedUid != null) - ? uidMatcher.test(cachedUid) - : entry.matches(element); - if (isMatch) { - if (addedToResult.add(entry)) { - result.add(entry); + if (groups != null) { + for (CollapsedStack entry : groups) { + if (activeSet.contains(entry)) { + if (addedToResult.add(entry)) { + result.add(entry); + } + entry.addIngredient(element); + matched = true; } - entry.addIngredient(element); - matched = true; } } if (!matched) { result.add(element); } - idx++; } // Remove empty collapsed stacks (shouldn't happen, but be safe) From 5c17aa8dc04e4603c4bf9ed7fd1ef4125d88b41b Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 25 Mar 2026 04:25:14 -0400 Subject: [PATCH 34/48] Fix api hidden items being shown When show all items was enabled with creative, we would show items API blacklisted unintentionally. --- src/main/java/mezz/jei/ingredients/IngredientListElement.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/ingredients/IngredientListElement.java b/src/main/java/mezz/jei/ingredients/IngredientListElement.java index 13a87d53b..9c2cb181c 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientListElement.java +++ b/src/main/java/mezz/jei/ingredients/IngredientListElement.java @@ -2,6 +2,7 @@ import com.google.common.collect.ImmutableSet; import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import mezz.jei.Internal; import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.api.ingredients.IIngredientRenderer; import mezz.jei.bookmarks.BookmarkItem; @@ -177,7 +178,8 @@ public boolean isVisible() { return true; } if (FMLLaunchHandler.side().isClient()) { - return Config.getShowHiddenIngredientsInCreative() && ProxyCommonClient.isCreative(); + return Config.getShowHiddenIngredientsInCreative() && ProxyCommonClient.isCreative() && + !Internal.getHelpers().getIngredientBlacklist().isIngredientBlacklistedByApi(ingredient); } return false; } From 3c4e3d973e03cfe28a48031c4f8d6d71a879fab5 Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 25 Mar 2026 04:28:41 -0400 Subject: [PATCH 35/48] Reload cache on show hidden in creative toggle. When a user toggles the setting to show blacklisted items in creative, the cache needs to be reloaded for the change to reflect in the itemlist. --- src/main/java/mezz/jei/config/Config.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index 447fcae5b..3f32ce114 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -566,7 +566,13 @@ private static boolean syncConfig() { values.tooltipShowRecipeBy = config.getBoolean(CATEGORY_MISC, "tooltipShowRecipeBy", defaultValues.tooltipShowRecipeBy); - values.showHiddenIngredientsInCreative = config.getBoolean(CATEGORY_MISC, "showHiddenIngredientsInCreative", defaultValues.showHiddenIngredientsInCreative); + { + boolean prev = values.showHiddenIngredientsInCreative; + values.showHiddenIngredientsInCreative = config.getBoolean(CATEGORY_MISC, "showHiddenIngredientsInCreative", defaultValues.showHiddenIngredientsInCreative); + if (prev != values.showHiddenIngredientsInCreative) { + needsReload = true; + } + } values.skipShowingProgressBar = config.getBoolean(CATEGORY_MISC, "skipShowingProgressBar", defaultValues.skipShowingProgressBar); From 572f332f16fb7fcfc087d0547f773387ba9bf9de Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 27 Mar 2026 05:46:47 -0400 Subject: [PATCH 36/48] Implement ICollapsibleGroupRegistry API Allow mods to register custom groups with HEI. --- gradle.properties | 2 +- .../jei/api/ICollapsibleGroupRegistry.java | 52 +++++++++++++++++ src/api/java/mezz/jei/api/IModPlugin.java | 11 ++++ .../collapsible/GuiCollapsibleGroups.java | 56 ++++++++++++++----- .../mezz/jei/ingredients/CollapsedStack.java | 28 ++++++++++ .../ingredients/CollapsedStackRegistry.java | 45 +++++++++++++++ .../jei/ingredients/IngredientFilter.java | 14 ++++- .../java/mezz/jei/startup/JeiStarter.java | 46 +++++++++++++++ src/main/resources/assets/jei/lang/en_us.lang | 1 + 9 files changed, 239 insertions(+), 16 deletions(-) create mode 100644 src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java diff --git a/gradle.properties b/gradle.properties index 8bd31a27e..67f7f5c2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ generate_javadocs_jar = false # Mod Information # HIGHLY RECOMMEND complying with SemVer for mod_version: https://semver.org/ -mod_version = 4.30.3 +mod_version = 4.30.4 root_package = mezz mod_id = jei mod_name = Had Enough Items diff --git a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java new file mode 100644 index 000000000..2471653c1 --- /dev/null +++ b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java @@ -0,0 +1,52 @@ +package mezz.jei.api; + +import net.minecraft.item.ItemStack; + +import java.util.Collection; +import java.util.function.Predicate; + +/** + * Registry for mods to define collapsible ingredient groups in the JEI ingredient list. + * Groups registered here appear with a "Mod" source tag in the Manage Groups screen. + * They can be toggled on/off by the user but cannot be edited or deleted. + *

+ * Obtain an instance via {@link IModPlugin#registerCollapsibleGroups(ICollapsibleGroupRegistry)}. + *

+ * Group IDs should include your mod ID to avoid conflicts (e.g. {@code "matteroverdrive:matter_dusts"}). + * + * @since HEI 4.30.4 + */ +public interface ICollapsibleGroupRegistry { + + /** + * Register a collapsible group for ItemStack ingredients using a predicate. + * Non-ItemStack ingredients are automatically excluded. + * + * @param id unique group ID, should be namespaced with your mod ID + * @param displayName localized display name shown in the groups screen + * @param matcher predicate that returns true for ItemStacks belonging to this group + */ + void addGroup(String id, String displayName, Predicate matcher); + + /** + * Register a collapsible group that matches any ingredient type using a predicate. + * Use this for fluids, custom ingredient types, or mixed groups. + * + * @param id unique group ID, should be namespaced with your mod ID + * @param displayName localized display name shown in the groups screen + * @param matcher predicate on the raw ingredient object + */ + void addGroupForType(String id, String displayName, Predicate matcher); + + /** + * Register a collapsible group containing specific ingredients. + * The collection may contain any mix of ItemStacks, FluidStacks, or other + * registered ingredient types. Each ingredient is resolved to its unique identifier + * at registration time. + * + * @param id unique group ID, should be namespaced with your mod ID + * @param displayName localized display name shown in the groups screen + * @param ingredients the specific ingredients belonging to this group + */ + void addGroup(String id, String displayName, Collection ingredients); +} diff --git a/src/api/java/mezz/jei/api/IModPlugin.java b/src/api/java/mezz/jei/api/IModPlugin.java index 8e93e240e..d02e16459 100644 --- a/src/api/java/mezz/jei/api/IModPlugin.java +++ b/src/api/java/mezz/jei/api/IModPlugin.java @@ -48,6 +48,17 @@ default void registerCategories(IRecipeCategoryRegistration registry) { } + /** + * Register collapsible ingredient groups provided by this mod. + * These appear in the "Manage Groups" screen tagged as "Mod" and can be toggled + * by the user but are not editable or deletable. + * + * @since HEI 4.30.4 + */ + default void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { + + } + /** * Register this mod plugin with the mod registry. */ diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 0e261953c..0d4da94fb 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -6,6 +6,7 @@ import mezz.jei.config.CustomGroupsConfig; import mezz.jei.gui.ingredients.IIngredientListElement; import mezz.jei.ingredients.CollapsedStack; +import mezz.jei.ingredients.CollapsedStack.GroupSource; import mezz.jei.ingredients.CollapsedStackRegistry; import mezz.jei.ingredients.IngredientFilter; import mezz.jei.util.Translator; @@ -113,7 +114,15 @@ private void rebuildCards() { for (CollapsedStack entry : registry.getCustomEntries()) { List> previewItems = getPreviewItems(entry); int itemCount = getMatchedItemCount(entry); - cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), true, + cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), GroupSource.CUSTOM, + !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); + } + + // Mod-registered groups + for (CollapsedStack entry : registry.getModEntries()) { + List> previewItems = getPreviewItems(entry); + int itemCount = getMatchedItemCount(entry); + cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), GroupSource.MOD, !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); } @@ -121,7 +130,7 @@ private void rebuildCards() { for (CollapsedStack entry : registry.getEntries()) { List> previewItems = getPreviewItems(entry); int itemCount = getMatchedItemCount(entry); - cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), false, + cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), GroupSource.DEFAULT, !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); } @@ -155,7 +164,7 @@ private void rebuildPageButtons() { : Translator.translateToLocal("jei.gui.collapsible.disabled"); this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 20, toggleLabel)); - if (card.isCustom) { + if (card.source == GroupSource.CUSTOM) { if (i == pendingDeleteIdx) { // Confirm row: ✔ Yes / ✗ No this.buttonList.add(new GuiButton(BTN_DELETE_CONFIRM_BASE + i, btnX, btnY + 22, 24, 20, @@ -243,7 +252,7 @@ protected void actionPerformed(GuiButton button) throws IOException { int idx = button.id - BTN_CONFIGURE_BASE; if (idx >= 0 && idx < cardEntries.size()) { GroupCardEntry card = cardEntries.get(idx); - if (card.isCustom) { + if (card.source == GroupSource.CUSTOM) { CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); if (customGroupsConfig != null) { for (CustomGroupsConfig.CustomGroup group : customGroupsConfig.getCustomGroups()) { @@ -261,7 +270,7 @@ protected void actionPerformed(GuiButton button) throws IOException { // Delete — first click: arm confirmation if (button.id >= BTN_DELETE_BASE && button.id < BTN_DELETE_CONFIRM_BASE) { int idx = button.id - BTN_DELETE_BASE; - if (idx >= 0 && idx < cardEntries.size() && cardEntries.get(idx).isCustom) { + if (idx >= 0 && idx < cardEntries.size() && cardEntries.get(idx).source == GroupSource.CUSTOM) { pendingDeleteIdx = idx; rebuildPageButtons(); } @@ -274,7 +283,7 @@ protected void actionPerformed(GuiButton button) throws IOException { pendingDeleteIdx = -1; if (idx >= 0 && idx < cardEntries.size()) { GroupCardEntry card = cardEntries.get(idx); - if (card.isCustom) { + if (card.source == GroupSource.CUSTOM) { CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); if (customGroupsConfig != null) { customGroupsConfig.removeGroup(card.id); @@ -324,7 +333,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { drawCard(card, cardX, cardY, layoutColWidth, mouseX, mouseY); // Draw "Delete?" label to the left of the ✔/✗ confirm buttons - if (i == pendingDeleteIdx && card.isCustom) { + if (i == pendingDeleteIdx && card.source == GroupSource.CUSTOM) { int btnX = cardX + layoutColWidth - 56; int btnY = cardY + 4; String confirmLabel = Translator.translateToLocal("jei.gui.collapsible.confirmDelete"); @@ -354,7 +363,12 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, int mouseY) { // Card background - int bgColor = card.isCustom ? 0x40336699 : 0x40444444; + int bgColor; + switch (card.source) { + case CUSTOM: bgColor = 0x40336699; break; + case MOD: bgColor = 0x40553366; break; + default: bgColor = 0x40444444; break; + } drawRect(x, y, x + width, y + CARD_HEIGHT, bgColor); // Border @@ -365,9 +379,23 @@ private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, drawVerticalLine(x + width - 1, y, y + CARD_HEIGHT - 1, borderColor); // Group name - String namePrefix = card.isCustom - ? "\u00A7e[" + Translator.translateToLocal("jei.gui.collapsible.customGroup") + "] \u00A7r" - : "\u00A77[" + Translator.translateToLocal("jei.gui.collapsible.defaultGroup") + "] \u00A7r"; + String sourceLabel; + String sourceColor; + switch (card.source) { + case CUSTOM: + sourceColor = "\u00A7e"; + sourceLabel = Translator.translateToLocal("jei.gui.collapsible.customGroup"); + break; + case MOD: + sourceColor = "\u00A7d"; + sourceLabel = Translator.translateToLocal("jei.gui.collapsible.modGroup"); + break; + default: + sourceColor = "\u00A77"; + sourceLabel = Translator.translateToLocal("jei.gui.collapsible.defaultGroup"); + break; + } + String namePrefix = sourceColor + "[" + sourceLabel + "] \u00A7r"; this.fontRenderer.drawStringWithShadow(namePrefix + card.displayName, x + 4, y + 4, 0xFFFFFF); // Item count @@ -607,16 +635,16 @@ public void onEditorClosed() { private static class GroupCardEntry { final String id; final String displayName; - final boolean isCustom; + final GroupSource source; boolean enabled; final List> previewItems; final int itemCount; int previewScrollRow = 0; - GroupCardEntry(String id, String displayName, boolean isCustom, boolean enabled, List> previewItems, int itemCount) { + GroupCardEntry(String id, String displayName, GroupSource source, boolean enabled, List> previewItems, int itemCount) { this.id = id; this.displayName = displayName; - this.isCustom = isCustom; + this.source = source; this.enabled = enabled; this.previewItems = previewItems; this.itemCount = itemCount; diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStack.java b/src/main/java/mezz/jei/ingredients/CollapsedStack.java index 67fae0600..b880f581e 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStack.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStack.java @@ -24,8 +24,16 @@ public class CollapsedStack { // Registered as IIngredientType for addon compatibility — addons expect every grid item to have a type public static final IIngredientType TYPE = () -> CollapsedStack.class; + /** Identifies who registered this group. */ + public enum GroupSource { + DEFAULT, + MOD, + CUSTOM + } + private final String id; private final String displayName; + private final GroupSource source; /** Matches against the raw ingredient object (any type). */ private final Predicate matcher; /** @@ -42,9 +50,17 @@ public class CollapsedStack { * Primary constructor — matcher receives the raw ingredient object. */ public CollapsedStack(String id, String displayName, Predicate matcher) { + this(id, displayName, matcher, GroupSource.DEFAULT); + } + + /** + * Constructor with explicit group source. + */ + public CollapsedStack(String id, String displayName, Predicate matcher, GroupSource source) { this.id = id; this.displayName = displayName; this.matcher = matcher; + this.source = source; this.expanded = false; this.ingredients = new ArrayList<>(); } @@ -58,6 +74,14 @@ public static CollapsedStack ofItemStack(String id, String displayName, Predicat ingredient -> ingredient instanceof ItemStack && stackMatcher.test((ItemStack) ingredient)); } + /** + * Convenience factory for ItemStack groups with an explicit source. + */ + public static CollapsedStack ofItemStack(String id, String displayName, Predicate stackMatcher, GroupSource source) { + return new CollapsedStack(id, displayName, + ingredient -> ingredient instanceof ItemStack && stackMatcher.test((ItemStack) ingredient), source); + } + // --- Group definition --- public String getId() { @@ -68,6 +92,10 @@ public String getDisplayName() { return displayName; } + public GroupSource getSource() { + return source; + } + public boolean isExpanded() { return expanded; } diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index ec16abfcb..94a517a0f 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -19,6 +19,7 @@ public class CollapsedStackRegistry { private static CollapsedStackRegistry instance; private final LinkedHashMap entries = new LinkedHashMap<>(); + private final List modEntries = new ArrayList<>(); private final List customEntries = new ArrayList<>(); private final Set disabledGroups = new HashSet<>(); @@ -69,6 +70,7 @@ public CollapsedStack getEntry(String id) { public void clear() { entries.clear(); + modEntries.clear(); } public Set getDisabledGroups() { @@ -88,6 +90,49 @@ public List getCustomEntries() { return customEntries; } + public List getModEntries() { + return modEntries; + } + + /** + * Register a mod-provided collapsible group matching ItemStack ingredients. + */ + public void addModGroup(String id, String displayName, Predicate matcher) { + modEntries.add(CollapsedStack.ofItemStack(id, displayName, matcher, CollapsedStack.GroupSource.MOD)); + } + + /** + * Register a mod-provided collapsible group matching any ingredient type. + */ + public void addModGroupForType(String id, String displayName, Predicate matcher) { + modEntries.add(new CollapsedStack(id, displayName, matcher, CollapsedStack.GroupSource.MOD)); + } + + /** + * Register a mod-provided collapsible group from a collection of specific ingredients. + * UIDs are computed at registration time and a fast-path UID matcher is set. + */ + public void addModGroupFromIngredients(String id, String displayName, Collection ingredients) { + Set exactUids = new HashSet<>(); + for (Object ingredient : ingredients) { + String uid = CollapsedStack.computeIngredientUid(ingredient); + if (uid != null) { + exactUids.add(uid); + } + } + if (exactUids.isEmpty()) { + Log.get().warn("Mod collapsible group '{}' has no valid ingredients, skipping", id); + return; + } + final Predicate uidPredicate = exactUids::contains; + CollapsedStack cs = new CollapsedStack(id, displayName, ingredient -> { + String uid = CollapsedStack.computeIngredientUid(ingredient); + return uid != null && uidPredicate.test(uid); + }, CollapsedStack.GroupSource.MOD); + cs.setUidMatcher(uidPredicate); + modEntries.add(cs); + } + /** * Load custom collapsible groups from the JSON config. * Creates CollapsedStack objects that match items by their unique identifier. diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index 5e3ef80fe..bf3886c82 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -153,6 +153,7 @@ private IdentityHashMap, List> getGrou CollapsedStackRegistry registry = CollapsedStackRegistry.getInstance(); List allEntries = new ArrayList<>(); allEntries.addAll(registry.getEntries()); + allEntries.addAll(registry.getModEntries()); allEntries.addAll(registry.getCustomEntries()); if (allEntries.isEmpty()) return groupMembershipCache; @@ -363,6 +364,11 @@ private List> withGroupNameMatches( matchingGroups.add(entry); } } + for (CollapsedStack entry : registry.getModEntries()) { + if (Translator.toLowercaseWithLocale(entry.getDisplayName()).contains(filterText)) { + matchingGroups.add(entry); + } + } for (CollapsedStack entry : registry.getCustomEntries()) { if (Translator.toLowercaseWithLocale(entry.getDisplayName()).contains(filterText)) { matchingGroups.add(entry); @@ -410,8 +416,9 @@ private List collapse(List ingredientList) { } CollapsedStackRegistry registry = CollapsedStackRegistry.getInstance(); Collection entries = registry.getEntries(); + List modEntries = registry.getModEntries(); List customEntries = registry.getCustomEntries(); - if (entries.isEmpty() && customEntries.isEmpty()) { + if (entries.isEmpty() && modEntries.isEmpty() && customEntries.isEmpty()) { return new ArrayList<>(ingredientList); } @@ -422,6 +429,11 @@ private List collapse(List ingredientList) { activeEntries.add(entry); } } + for (CollapsedStack entry : modEntries) { + if (registry.isGroupEnabled(entry.getId())) { + activeEntries.add(entry); + } + } for (CollapsedStack entry : customEntries) { if (registry.isGroupEnabled(entry.getId())) { activeEntries.add(entry); diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index c0c873cf2..80054d22f 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -2,6 +2,7 @@ import mezz.jei.Internal; import mezz.jei.Tags; +import mezz.jei.api.ICollapsibleGroupRegistry; import mezz.jei.api.IJeiRuntime; import mezz.jei.api.IModPlugin; import mezz.jei.api.gui.IAdvancedGuiHandler; @@ -35,9 +36,11 @@ import mezz.jei.util.Log; import mezz.jei.util.LoggedTimer; import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; import net.minecraft.item.ItemMonsterPlacer; import net.minecraftforge.fml.common.ProgressManager; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -101,6 +104,7 @@ public void load(List plugins, Textures textures, boolean recipesOnl } registerDefaultCollapsibleGroups(); + registerModCollapsibleGroups(plugins); { CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); @@ -366,4 +370,46 @@ private static void registerDefaultCollapsibleGroups() { stack -> stack.getItem() instanceof ItemMonsterPlacer); } + private static void registerModCollapsibleGroups(List plugins) { + CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); + + ICollapsibleGroupRegistry apiRegistry = new ICollapsibleGroupRegistry() { + @Override + public void addGroup(String id, String displayName, java.util.function.Predicate matcher) { + registry.addModGroup(id, displayName, matcher); + } + + @Override + public void addGroupForType(String id, String displayName, java.util.function.Predicate matcher) { + registry.addModGroupForType(id, displayName, matcher); + } + + @Override + public void addGroup(String id, String displayName, Collection ingredients) { + registry.addModGroupFromIngredients(id, displayName, ingredients); + } + }; + + if (Config.skipShowingProgressBar()) { + for (IModPlugin plugin : plugins) { + try { + plugin.registerCollapsibleGroups(apiRegistry); + } catch (RuntimeException | LinkageError e) { + Log.get().error("Failed to register collapsible groups for plugin: {}", plugin.getClass(), e); + } + } + } else { + ProgressManager.ProgressBar bar = ProgressManager.push("Registering collapsible groups", plugins.size()); + for (IModPlugin plugin : plugins) { + try { + bar.step(plugin.getClass().getName()); + plugin.registerCollapsibleGroups(apiRegistry); + } catch (RuntimeException | LinkageError e) { + Log.get().error("Failed to register collapsible groups for plugin: {}", plugin.getClass(), e); + } + } + ProgressManager.pop(bar); + } + } + } diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index b8b58b2c1..be83a97ad 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -227,6 +227,7 @@ jei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user jei.gui.collapsible.enabled=Enabled jei.gui.collapsible.disabled=Disabled jei.gui.collapsible.customGroup=Custom +jei.gui.collapsible.modGroup=Mod jei.gui.collapsible.defaultGroup=Default jei.gui.collapsible.itemCount=%d items jei.gui.collapsible.editor.title=Selected Items From d62d71489012458c48b1f188705dff387fd50118 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 27 Mar 2026 20:30:06 -0400 Subject: [PATCH 37/48] Collapsible Stack API change & Manage Group merge Condensed API calls to a single addGroup, use IIngredientType predicate. Collapsible Groups Manager now condenses group calls with the same ID as a single card. --- gradle.properties | 2 +- .../jei/api/ICollapsibleGroupRegistry.java | 41 ++++-------- src/api/java/mezz/jei/api/IModPlugin.java | 2 +- .../collapsible/GuiCollapsibleGroups.java | 65 ++++++++++++------- .../ingredients/CollapsedStackRegistry.java | 45 +++---------- .../java/mezz/jei/startup/JeiStarter.java | 18 ++--- 6 files changed, 68 insertions(+), 105 deletions(-) diff --git a/gradle.properties b/gradle.properties index 67f7f5c2c..d437f7282 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ generate_javadocs_jar = false # Mod Information # HIGHLY RECOMMEND complying with SemVer for mod_version: https://semver.org/ -mod_version = 4.30.4 +mod_version = 4.30.5 root_package = mezz mod_id = jei mod_name = Had Enough Items diff --git a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java index 2471653c1..9443d0d79 100644 --- a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java +++ b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java @@ -1,8 +1,7 @@ package mezz.jei.api; -import net.minecraft.item.ItemStack; +import mezz.jei.api.recipe.IIngredientType; -import java.util.Collection; import java.util.function.Predicate; /** @@ -13,40 +12,24 @@ * Obtain an instance via {@link IModPlugin#registerCollapsibleGroups(ICollapsibleGroupRegistry)}. *

* Group IDs should include your mod ID to avoid conflicts (e.g. {@code "matteroverdrive:matter_dusts"}). + * To create a group spanning multiple ingredient types, call {@link #addGroup} once per type + * using the same {@code id}. * - * @since HEI 4.30.4 + * @since HEI 4.30.5 */ public interface ICollapsibleGroupRegistry { /** - * Register a collapsible group for ItemStack ingredients using a predicate. - * Non-ItemStack ingredients are automatically excluded. + * Register a collapsible group for a specific ingredient type using a type-safe predicate. + *

+ * Use {@code VanillaTypes.ITEM} for ItemStack groups and {@code VanillaTypes.FLUID} for + * FluidStack groups. Third-party ingredient types registered via {@link IIngredientRegistry} + * are also supported. * * @param id unique group ID, should be namespaced with your mod ID * @param displayName localized display name shown in the groups screen - * @param matcher predicate that returns true for ItemStacks belonging to this group + * @param type the ingredient type (e.g. {@code VanillaTypes.ITEM}) + * @param matcher predicate receiving a fully-typed {@code V} — no casting needed */ - void addGroup(String id, String displayName, Predicate matcher); - - /** - * Register a collapsible group that matches any ingredient type using a predicate. - * Use this for fluids, custom ingredient types, or mixed groups. - * - * @param id unique group ID, should be namespaced with your mod ID - * @param displayName localized display name shown in the groups screen - * @param matcher predicate on the raw ingredient object - */ - void addGroupForType(String id, String displayName, Predicate matcher); - - /** - * Register a collapsible group containing specific ingredients. - * The collection may contain any mix of ItemStacks, FluidStacks, or other - * registered ingredient types. Each ingredient is resolved to its unique identifier - * at registration time. - * - * @param id unique group ID, should be namespaced with your mod ID - * @param displayName localized display name shown in the groups screen - * @param ingredients the specific ingredients belonging to this group - */ - void addGroup(String id, String displayName, Collection ingredients); + void addGroup(String id, String displayName, IIngredientType type, Predicate matcher); } diff --git a/src/api/java/mezz/jei/api/IModPlugin.java b/src/api/java/mezz/jei/api/IModPlugin.java index d02e16459..c1c63b7fc 100644 --- a/src/api/java/mezz/jei/api/IModPlugin.java +++ b/src/api/java/mezz/jei/api/IModPlugin.java @@ -53,7 +53,7 @@ default void registerCategories(IRecipeCategoryRegistration registry) { * These appear in the "Manage Groups" screen tagged as "Mod" and can be toggled * by the user but are not editable or deletable. * - * @since HEI 4.30.4 + * @since HEI 4.30.5 */ default void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 0d4da94fb..829960873 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -111,28 +111,13 @@ private void rebuildCards() { CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); // Custom groups come first (like REI) - for (CollapsedStack entry : registry.getCustomEntries()) { - List> previewItems = getPreviewItems(entry); - int itemCount = getMatchedItemCount(entry); - cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), GroupSource.CUSTOM, - !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); - } + addCardsMergedById(registry.getCustomEntries(), GroupSource.CUSTOM, registry.getDisabledGroups()); - // Mod-registered groups - for (CollapsedStack entry : registry.getModEntries()) { - List> previewItems = getPreviewItems(entry); - int itemCount = getMatchedItemCount(entry); - cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), GroupSource.MOD, - !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); - } + // Mod-registered groups (same ID can be registered multiple times for different ingredient types) + addCardsMergedById(registry.getModEntries(), GroupSource.MOD, registry.getDisabledGroups()); // Default groups - for (CollapsedStack entry : registry.getEntries()) { - List> previewItems = getPreviewItems(entry); - int itemCount = getMatchedItemCount(entry); - cardEntries.add(new GroupCardEntry(entry.getId(), entry.getDisplayName(), GroupSource.DEFAULT, - !registry.getDisabledGroups().contains(entry.getId()), previewItems, itemCount)); - } + addCardsMergedById(registry.getEntries(), GroupSource.DEFAULT, registry.getDisabledGroups()); totalPages = Math.max(1, (cardEntries.size() + cardsPerPage - 1) / cardsPerPage); if (currentPage >= totalPages) { @@ -140,6 +125,25 @@ private void rebuildCards() { } } + private void addCardsMergedById(Collection entries, GroupSource source, Set disabledGroups) { + Map> groupedById = new LinkedHashMap<>(); + Map displayNamesById = new HashMap<>(); + + for (CollapsedStack entry : entries) { + groupedById.computeIfAbsent(entry.getId(), k -> new ArrayList<>()).add(entry); + displayNamesById.putIfAbsent(entry.getId(), entry.getDisplayName()); + } + + for (Map.Entry> groupedEntry : groupedById.entrySet()) { + String id = groupedEntry.getKey(); + List groupedStacks = groupedEntry.getValue(); + List> previewItems = getPreviewItems(groupedStacks); + int itemCount = getMatchedItemCount(groupedStacks); + cardEntries.add(new GroupCardEntry(id, displayNamesById.get(id), source, + !disabledGroups.contains(id), previewItems, itemCount)); + } + } + private void rebuildPageButtons() { // Remove old card-specific and page buttons buttonList.removeIf(b -> b.id >= BTN_PREV_PAGE); @@ -559,15 +563,16 @@ protected void keyTyped(char typedChar, int keyCode) throws IOException { * Get up to PREVIEW_FETCH_MAX preview elements for a collapsible entry, * returning the raw IIngredientListElement so each type renders via its own renderer. */ - private List> getPreviewItems(CollapsedStack entry) { + private List> getPreviewItems(List entries) { List> items = new ArrayList<>(); if (!Internal.hasIngredientFilter()) { return items; } IngredientFilter filter = Internal.getIngredientFilter(); - List ingredientList = filter.getIngredientList(""); + @SuppressWarnings({"unchecked", "rawtypes"}) + List> ingredientList = (List>) (List) filter.getIngredientList(""); for (IIngredientListElement element : ingredientList) { - if (entry.matches(element)) { + if (matchesAny(entries, element)) { items.add(element); if (items.size() >= PREVIEW_FETCH_MAX) { break; @@ -609,21 +614,31 @@ private void renderIngredientTooltip(IIngredientListElement element, int /** * Count matched items for display. */ - private int getMatchedItemCount(CollapsedStack entry) { + private int getMatchedItemCount(List entries) { if (!Internal.hasIngredientFilter()) { return 0; } IngredientFilter filter = Internal.getIngredientFilter(); - List ingredientList = filter.getIngredientList(""); + @SuppressWarnings({"unchecked", "rawtypes"}) + List> ingredientList = (List>) (List) filter.getIngredientList(""); int count = 0; for (IIngredientListElement element : ingredientList) { - if (entry.matches(element)) { + if (matchesAny(entries, element)) { count++; } } return count; } + private static boolean matchesAny(List entries, IIngredientListElement element) { + for (CollapsedStack entry : entries) { + if (entry.matches(element)) { + return true; + } + } + return false; + } + /** * Called when returning from the editor screen to refresh the card list. */ diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index 94a517a0f..c9927ccc3 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -95,42 +95,17 @@ public List getModEntries() { } /** - * Register a mod-provided collapsible group matching ItemStack ingredients. + * Register a mod-provided collapsible group for a specific ingredient type. + * Uses {@link mezz.jei.api.recipe.IIngredientType#getIngredientClass()} to guard the + * predicate, so {@code matcher} receives a fully-typed {@code V} with no unchecked cast. */ - public void addModGroup(String id, String displayName, Predicate matcher) { - modEntries.add(CollapsedStack.ofItemStack(id, displayName, matcher, CollapsedStack.GroupSource.MOD)); - } - - /** - * Register a mod-provided collapsible group matching any ingredient type. - */ - public void addModGroupForType(String id, String displayName, Predicate matcher) { - modEntries.add(new CollapsedStack(id, displayName, matcher, CollapsedStack.GroupSource.MOD)); - } - - /** - * Register a mod-provided collapsible group from a collection of specific ingredients. - * UIDs are computed at registration time and a fast-path UID matcher is set. - */ - public void addModGroupFromIngredients(String id, String displayName, Collection ingredients) { - Set exactUids = new HashSet<>(); - for (Object ingredient : ingredients) { - String uid = CollapsedStack.computeIngredientUid(ingredient); - if (uid != null) { - exactUids.add(uid); - } - } - if (exactUids.isEmpty()) { - Log.get().warn("Mod collapsible group '{}' has no valid ingredients, skipping", id); - return; - } - final Predicate uidPredicate = exactUids::contains; - CollapsedStack cs = new CollapsedStack(id, displayName, ingredient -> { - String uid = CollapsedStack.computeIngredientUid(ingredient); - return uid != null && uidPredicate.test(uid); - }, CollapsedStack.GroupSource.MOD); - cs.setUidMatcher(uidPredicate); - modEntries.add(cs); + public void addModGroup(String id, String displayName, + mezz.jei.api.recipe.IIngredientType type, Predicate matcher) { + Class ingredientClass = type.getIngredientClass(); + modEntries.add(new CollapsedStack(id, displayName, ingredient -> { + if (!ingredientClass.isInstance(ingredient)) return false; + return matcher.test(ingredientClass.cast(ingredient)); + }, CollapsedStack.GroupSource.MOD)); } /** diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index 80054d22f..8d3336852 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -36,11 +36,9 @@ import mezz.jei.util.Log; import mezz.jei.util.LoggedTimer; import net.minecraft.init.Items; -import net.minecraft.item.ItemStack; import net.minecraft.item.ItemMonsterPlacer; import net.minecraftforge.fml.common.ProgressManager; -import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -375,18 +373,10 @@ private static void registerModCollapsibleGroups(List plugins) { ICollapsibleGroupRegistry apiRegistry = new ICollapsibleGroupRegistry() { @Override - public void addGroup(String id, String displayName, java.util.function.Predicate matcher) { - registry.addModGroup(id, displayName, matcher); - } - - @Override - public void addGroupForType(String id, String displayName, java.util.function.Predicate matcher) { - registry.addModGroupForType(id, displayName, matcher); - } - - @Override - public void addGroup(String id, String displayName, Collection ingredients) { - registry.addModGroupFromIngredients(id, displayName, ingredients); + public void addGroup(String id, String displayName, + mezz.jei.api.recipe.IIngredientType type, + java.util.function.Predicate matcher) { + registry.addModGroup(id, displayName, type, matcher); } }; From ee6c907e02e582becdf92986d5bb0ce4d65caef2 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 27 Mar 2026 22:39:40 -0400 Subject: [PATCH 38/48] Update ICollapsibleGroupRegistry Javadoc. --- .../jei/api/ICollapsibleGroupRegistry.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java index 9443d0d79..a9d9cfc79 100644 --- a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java +++ b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java @@ -1,35 +1,42 @@ package mezz.jei.api; +import mezz.jei.api.ingredients.IIngredientRegistry; import mezz.jei.api.recipe.IIngredientType; import java.util.function.Predicate; /** - * Registry for mods to define collapsible ingredient groups in the JEI ingredient list. - * Groups registered here appear with a "Mod" source tag in the Manage Groups screen. + * Registry for mods to define collapsible groups in the HEI ingredient list. * They can be toggled on/off by the user but cannot be edited or deleted. - *

+ * First two items are shown based on registration order, position of group is based on first item. + * * Obtain an instance via {@link IModPlugin#registerCollapsibleGroups(ICollapsibleGroupRegistry)}. - *

- * Group IDs should include your mod ID to avoid conflicts (e.g. {@code "matteroverdrive:matter_dusts"}). + * * To create a group spanning multiple ingredient types, call {@link #addGroup} once per type * using the same {@code id}. + * + * EXAMPLE : + * @Override + * public void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { + * registry.addGroup( + * "matteroverdrive:colored_floor_tile", + * I18n.format("tile.decorative.floor_tile.name"), + * VanillaTypes.ITEM, + * stack -> Block.getBlockFromItem(stack.getItem()) == MatterOverdrive.BLOCKS.decorative_floor_tile); * * @since HEI 4.30.5 */ public interface ICollapsibleGroupRegistry { /** - * Register a collapsible group for a specific ingredient type using a type-safe predicate. - *

* Use {@code VanillaTypes.ITEM} for ItemStack groups and {@code VanillaTypes.FLUID} for * FluidStack groups. Third-party ingredient types registered via {@link IIngredientRegistry} * are also supported. * - * @param id unique group ID, should be namespaced with your mod ID - * @param displayName localized display name shown in the groups screen - * @param type the ingredient type (e.g. {@code VanillaTypes.ITEM}) - * @param matcher predicate receiving a fully-typed {@code V} — no casting needed + * @param id Unique group ID, should be namespaced with your mod ID. + * @param displayName Localized display name shown in the groups screen. + * @param type The ingredient type (e.g. {@code VanillaTypes.ITEM}). + * @param matcher Predicate receiving a fully-typed {@code V} — no casting needed. */ void addGroup(String id, String displayName, IIngredientType type, Predicate matcher); } From 5049bb02aac2967d6aa8db0c875bce39c7553e10 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sat, 28 Mar 2026 20:33:03 -0400 Subject: [PATCH 39/48] Change ICollapsibleGroupRegistry to take lang key Changed ICollapsibleGroupRegistry API to accept the untranslated lang key instead of a pre-resolved name, we now translate it on the API end. --- .../mezz/jei/api/ICollapsibleGroupRegistry.java | 14 +++++++------- src/main/java/mezz/jei/startup/JeiStarter.java | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java index a9d9cfc79..4b649462e 100644 --- a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java +++ b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java @@ -13,14 +13,14 @@ * Obtain an instance via {@link IModPlugin#registerCollapsibleGroups(ICollapsibleGroupRegistry)}. * * To create a group spanning multiple ingredient types, call {@link #addGroup} once per type - * using the same {@code id}. + * using the same {@code id}. Multiple calls of the same ID will be merged as one. * * EXAMPLE : * @Override * public void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { * registry.addGroup( * "matteroverdrive:colored_floor_tile", - * I18n.format("tile.decorative.floor_tile.name"), + * "tile.decorative.floor_tile.name", * VanillaTypes.ITEM, * stack -> Block.getBlockFromItem(stack.getItem()) == MatterOverdrive.BLOCKS.decorative_floor_tile); * @@ -33,10 +33,10 @@ public interface ICollapsibleGroupRegistry { * FluidStack groups. Third-party ingredient types registered via {@link IIngredientRegistry} * are also supported. * - * @param id Unique group ID, should be namespaced with your mod ID. - * @param displayName Localized display name shown in the groups screen. - * @param type The ingredient type (e.g. {@code VanillaTypes.ITEM}). - * @param matcher Predicate receiving a fully-typed {@code V} — no casting needed. + * @param id Unique group ID, should be namespaced with your mod ID. + * @param langKey Unlocalized translation key for the group name (e.g. {@code "tile.mymod.name"}). + * @param type The ingredient type (e.g. {@code VanillaTypes.ITEM}). + * @param matcher Predicate receiving a fully-typed {@code V} — no casting needed. */ - void addGroup(String id, String displayName, IIngredientType type, Predicate matcher); + void addGroup(String id, String langKey, IIngredientType type, Predicate matcher); } diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index 8d3336852..7bdd3a2f3 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -23,6 +23,7 @@ import mezz.jei.gui.recipes.RecipesGui; import mezz.jei.gui.textures.Textures; import mezz.jei.ingredients.IngredientBlacklistInternal; +import mezz.jei.util.Translator; import mezz.jei.ingredients.IngredientFilter; import mezz.jei.ingredients.IngredientListElementFactory; import mezz.jei.ingredients.IngredientRegistry; @@ -373,10 +374,10 @@ private static void registerModCollapsibleGroups(List plugins) { ICollapsibleGroupRegistry apiRegistry = new ICollapsibleGroupRegistry() { @Override - public void addGroup(String id, String displayName, + public void addGroup(String id, String langKey, mezz.jei.api.recipe.IIngredientType type, java.util.function.Predicate matcher) { - registry.addModGroup(id, displayName, type, matcher); + registry.addModGroup(id, Translator.translateToLocal(langKey), type, matcher); } }; From d59cf57455d3a9ea557a46fca8d352c3ffa8b090 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 07:09:10 -0400 Subject: [PATCH 40/48] ICollapsibleGroup Builder. Change from an overload to a builder. --- gradle.properties | 2 +- .../jei/api/ICollapsibleGroupRegistry.java | 102 ++++++++++++++--- src/api/java/mezz/jei/api/IModPlugin.java | 7 +- .../ingredients/CollapsedStackRegistry.java | 16 +-- .../java/mezz/jei/startup/JeiStarter.java | 103 +++++++++++++++++- 5 files changed, 197 insertions(+), 33 deletions(-) diff --git a/gradle.properties b/gradle.properties index d437f7282..2ef4b8cc3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ generate_javadocs_jar = false # Mod Information # HIGHLY RECOMMEND complying with SemVer for mod_version: https://semver.org/ -mod_version = 4.30.5 +mod_version = 4.30.0 root_package = mezz mod_id = jei mod_name = Had Enough Items diff --git a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java index 4b649462e..6674768d4 100644 --- a/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java +++ b/src/api/java/mezz/jei/api/ICollapsibleGroupRegistry.java @@ -8,35 +8,103 @@ /** * Registry for mods to define collapsible groups in the HEI ingredient list. * They can be toggled on/off by the user but cannot be edited or deleted. - * First two items are shown based on registration order, position of group is based on first item. - * + * * Obtain an instance via {@link IModPlugin#registerCollapsibleGroups(ICollapsibleGroupRegistry)}. + * + * EXAMPLE 1 — Group with filtered items: + * + * @Override + * public void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { + * registry.newGroup("matteroverdrive:colored_floor_tile", "tile.decorative.floor_tile.name") + * .addAny(VanillaTypes.ITEM, + * stack -> Block.getBlockFromItem(stack.getItem()) == MatterOverdrive.BLOCKS.decorative_floor_tile); + * } * - * To create a group spanning multiple ingredient types, call {@link #addGroup} once per type - * using the same {@code id}. Multiple calls of the same ID will be merged as one. + * + * EXAMPLE 2 — Group mixing exact items, fluids, and all of a type: * - * EXAMPLE : * @Override * public void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { - * registry.addGroup( - * "matteroverdrive:colored_floor_tile", - * "tile.decorative.floor_tile.name", - * VanillaTypes.ITEM, - * stack -> Block.getBlockFromItem(stack.getItem()) == MatterOverdrive.BLOCKS.decorative_floor_tile); + * registry.newGroup("mymod:my_group", "group.mymod.my_group") + * .add(new ItemStack(MyMod.Items.PICKAXE)) // add one exact item + * .add(new ItemStack(MyMod.Blocks.MY_BLOCK, 1, 1)) // add a specific block variant (meta=1) + * .add(FluidRegistry.getFluidStack("water", 1000)) // add an exact fluid + * .addAllOf(MyMod.CUSTOM_INGREDIENT_TYPE); // add every ingredient of a custom type + * } + * * - * @since HEI 4.30.5 + * EXAMPLE 3 — Multiple groups from one plugin: + * + * @Override + * public void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { + * registry.newGroup("mymod:ores", "group.mymod.ores") + * .addAny(VanillaTypes.ITEM, stack -> isOre(stack)); + * + * registry.newGroup("mymod:gems", "group.mymod.gems") + * .addAny(VanillaTypes.ITEM, stack -> isGem(stack)); + * } + * + * + * @since HEI 4.30.0 */ public interface ICollapsibleGroupRegistry { /** - * Use {@code VanillaTypes.ITEM} for ItemStack groups and {@code VanillaTypes.FLUID} for - * FluidStack groups. Third-party ingredient types registered via {@link IIngredientRegistry} - * are also supported. + * Creates (or retrieves) a mod collapsible group builder. + * + * Multiple calls with the same {@code id} return a builder for the same logical group. * * @param id Unique group ID, should be namespaced with your mod ID. * @param langKey Unlocalized translation key for the group name (e.g. {@code "tile.mymod.name"}). - * @param type The ingredient type (e.g. {@code VanillaTypes.ITEM}). - * @param matcher Predicate receiving a fully-typed {@code V} — no casting needed. */ - void addGroup(String id, String langKey, IIngredientType type, Predicate matcher); + CollapsibleGroupBuilder newGroup(String id, String langKey); + + interface CollapsibleGroupBuilder { + /** + * Add one exact ingredient to the group. + * + * The backend resolves this ingredient to its unique id and matches by id. + * Works with ItemStacks, FluidStacks, or any registered ingredient type. + * + * @param ingredient the ingredient to add (e.g. new ItemStack(Items.APPLE)) + * @return this builder for chaining + */ + CollapsibleGroupBuilder add(Object ingredient); + + /** + * Add multiple exact ingredients to the group. Equivalent to calling {@link #add(Object)} + * for each element in order. + * + * @param ingredients varargs list of ingredients to add + * @return this builder for chaining + */ + CollapsibleGroupBuilder add(Object... ingredients); + + /** + * Add every ingredient of the given type(s) to the group — no filtering applied. + * + * Use this for custom ingredient types where you want to collapse all registered + * instances into one group. Third-party ingredient types registered via + * {@link IIngredientRegistry} are supported. + * + *

Note: passing {@code VanillaTypes.ITEM} or {@code VanillaTypes.FLUID} will match + * every item or fluid in the game. Prefer {@link #addAny} with a predicate + * when you only want a subset. + * + * @param types the ingredient type(s) whose every instance should be included + * @return this builder for chaining + */ + CollapsibleGroupBuilder addAllOf(IIngredientType... types); + + /** + * Add any ingredient of a given type that matches the provided predicate filter. + * + * Use this when you need to match items by condition (e.g. "all tools", "all ores", etc). + * + * @param type The ingredient type (e.g. {@code VanillaTypes.ITEM}). + * @param filter Predicate receiving a fully-typed {@code V} — no casting needed. + * @return this builder for chaining + */ + CollapsibleGroupBuilder addAny(IIngredientType type, Predicate filter); + } } diff --git a/src/api/java/mezz/jei/api/IModPlugin.java b/src/api/java/mezz/jei/api/IModPlugin.java index c1c63b7fc..b9b193e00 100644 --- a/src/api/java/mezz/jei/api/IModPlugin.java +++ b/src/api/java/mezz/jei/api/IModPlugin.java @@ -52,8 +52,13 @@ default void registerCategories(IRecipeCategoryRegistration registry) { * Register collapsible ingredient groups provided by this mod. * These appear in the "Manage Groups" screen tagged as "Mod" and can be toggled * by the user but are not editable or deletable. + *

+ * Use {@link ICollapsibleGroupRegistry#newGroup(String, String)} to create a builder + * and call its methods to define the group's members. See {@link ICollapsibleGroupRegistry} + * for full usage examples. * - * @since HEI 4.30.5 + * @param registry the registry used to create collapsible group builders + * @since HEI 4.30.0 */ default void registerCollapsibleGroups(ICollapsibleGroupRegistry registry) { diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java index c9927ccc3..9c67689d4 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackRegistry.java @@ -95,17 +95,13 @@ public List getModEntries() { } /** - * Register a mod-provided collapsible group for a specific ingredient type. - * Uses {@link mezz.jei.api.recipe.IIngredientType#getIngredientClass()} to guard the - * predicate, so {@code matcher} receives a fully-typed {@code V} with no unchecked cast. + * Register a mod-provided collapsible group. + * The matcher may combine exact ingredient matches, type-wide matches, and custom predicates. */ - public void addModGroup(String id, String displayName, - mezz.jei.api.recipe.IIngredientType type, Predicate matcher) { - Class ingredientClass = type.getIngredientClass(); - modEntries.add(new CollapsedStack(id, displayName, ingredient -> { - if (!ingredientClass.isInstance(ingredient)) return false; - return matcher.test(ingredientClass.cast(ingredient)); - }, CollapsedStack.GroupSource.MOD)); + public CollapsedStack addModGroup(String id, String displayName, Predicate matcher) { + CollapsedStack group = new CollapsedStack(id, displayName, matcher, CollapsedStack.GroupSource.MOD); + modEntries.add(group); + return group; } /** diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index 7bdd3a2f3..4f822a2ba 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -41,8 +41,12 @@ import net.minecraftforge.fml.common.ProgressManager; import java.util.Iterator; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; public class JeiStarter { private boolean started; @@ -371,13 +375,68 @@ private static void registerDefaultCollapsibleGroups() { private static void registerModCollapsibleGroups(List plugins) { CollapsedStackRegistry registry = Internal.getCollapsedStackRegistry(); + Map groupsById = new HashMap<>(); ICollapsibleGroupRegistry apiRegistry = new ICollapsibleGroupRegistry() { @Override - public void addGroup(String id, String langKey, - mezz.jei.api.recipe.IIngredientType type, - java.util.function.Predicate matcher) { - registry.addModGroup(id, Translator.translateToLocal(langKey), type, matcher); + public CollapsibleGroupBuilder newGroup(String id, String langKey) { + final ModGroupBuilderState state = groupsById.computeIfAbsent(id, key -> { + ModGroupBuilderState created = new ModGroupBuilderState(); + // No uidMatcher yet — installed after all plugins finish registering. + // This avoids IngredientFilter using an empty uid fast-path that would + // short-circuit addAny/addAllOf predicates and cause them to never match. + created.registeredStack = registry.addModGroup(id, Translator.translateToLocal(langKey), created::matches); + return created; + }); + + return new CollapsibleGroupBuilder() { + @Override + public CollapsibleGroupBuilder add(Object ingredient) { + if (ingredient != null) { + String uid = mezz.jei.ingredients.CollapsedStack.computeIngredientUid(ingredient); + if (uid != null) { + state.exactUids.add(uid); + } + } + return this; + } + + @Override + public CollapsibleGroupBuilder add(Object... ingredients) { + if (ingredients != null) { + for (Object ingredient : ingredients) { + add(ingredient); + } + } + return this; + } + + @Override + public CollapsibleGroupBuilder addAllOf(mezz.jei.api.recipe.IIngredientType... types) { + if (types != null) { + for (mezz.jei.api.recipe.IIngredientType type : types) { + if (type != null) { + state.allOfTypes.add(type.getIngredientClass()); + } + } + } + return this; + } + + @Override + public CollapsibleGroupBuilder addAny(mezz.jei.api.recipe.IIngredientType type, Predicate filter) { + if (type != null && filter != null) { + Class ingredientClass = type.getIngredientClass(); + state.typedPredicates.add(ingredient -> { + if (!ingredientClass.isInstance(ingredient)) { + return false; + } + return filter.test(ingredientClass.cast(ingredient)); + }); + } + return this; + } + }; } }; @@ -401,6 +460,42 @@ public void addGroup(String id, String langKey, } ProgressManager.pop(bar); } + + for (ModGroupBuilderState state : groupsById.values()) { + if (!state.exactUids.isEmpty() && state.registeredStack != null) { + state.registeredStack.setUidMatcher(state::matchesUid); + } + } + } + + private static class ModGroupBuilderState { + final Set exactUids = new HashSet<>(); + final Set> allOfTypes = new HashSet<>(); + final List> typedPredicates = new java.util.ArrayList<>(); + /** The CollapsedStack registered in the registry — uid matcher installed post-loop. */ + mezz.jei.ingredients.CollapsedStack registeredStack; + + boolean matchesUid(String uid) { + return exactUids.contains(uid); + } + + boolean matches(Object ingredient) { + String uid = mezz.jei.ingredients.CollapsedStack.computeIngredientUid(ingredient); + if (uid != null && exactUids.contains(uid)) { + return true; + } + for (Class ingredientClass : allOfTypes) { + if (ingredientClass.isInstance(ingredient)) { + return true; + } + } + for (Predicate predicate : typedPredicates) { + if (predicate.test(ingredient)) { + return true; + } + } + return false; + } } } From 7a76170151163965b726c72a48c96844d0e92564 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 08:26:12 -0400 Subject: [PATCH 41/48] Fix config imports --- src/main/java/mezz/jei/config/Config.java | 3 ++- src/main/java/mezz/jei/config/ConfigValues.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index 3f32ce114..966acafb7 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.util.*; import java.util.List; +import java.util.ArrayList; public final class Config { private static final String configKeyPrefix = "config.jei"; @@ -593,7 +594,7 @@ private static boolean syncConfig() { values.collapsedClickAction = config.getEnum("collapsedClickAction", CATEGORY_COLLAPSIBLE, defaultValues.collapsedClickAction, CollapsedClickAction.values()); // Explicit property order so the GUI shows collapsibleGroupsEnabled first, then collapseOnClose. - java.util.List collapsibleOrder = new java.util.ArrayList<>(); + List collapsibleOrder = new ArrayList<>(); collapsibleOrder.add("collapsibleGroupsEnabled"); collapsibleOrder.add("collapseOnClose"); collapsibleOrder.add("collapsedClickAction"); diff --git a/src/main/java/mezz/jei/config/ConfigValues.java b/src/main/java/mezz/jei/config/ConfigValues.java index 3b94fcc54..50fced31d 100644 --- a/src/main/java/mezz/jei/config/ConfigValues.java +++ b/src/main/java/mezz/jei/config/ConfigValues.java @@ -1,5 +1,6 @@ package mezz.jei.config; +import mezz.jei.util.CollapsedClickAction; import mezz.jei.util.GiveMode; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; @@ -59,6 +60,6 @@ public class ConfigValues { // collapsible groups public boolean collapsibleGroupsEnabled = true; public boolean collapseOnClose = false; - public mezz.jei.util.CollapsedClickAction collapsedClickAction = mezz.jei.util.CollapsedClickAction.OPEN_GROUP; + public CollapsedClickAction collapsedClickAction = CollapsedClickAction.OPEN_GROUP; public Set disabledGroups = new HashSet<>(); } From 96d82f88bcb37529a83f4a4e8261411dc52d890f Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 08:38:47 -0400 Subject: [PATCH 42/48] ArrayList -> LinkedHashMap --- .../mezz/jei/config/CustomGroupsConfig.java | 42 +++++++++++-------- .../collapsible/GuiCollapsibleGroups.java | 9 ++-- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/main/java/mezz/jei/config/CustomGroupsConfig.java b/src/main/java/mezz/jei/config/CustomGroupsConfig.java index a67ae65df..5b81f930f 100644 --- a/src/main/java/mezz/jei/config/CustomGroupsConfig.java +++ b/src/main/java/mezz/jei/config/CustomGroupsConfig.java @@ -10,7 +10,11 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * Manages custom collapsible groups persistence as JSON. @@ -21,58 +25,60 @@ public class CustomGroupsConfig { private static final Type GROUP_LIST_TYPE = new TypeToken>() {}.getType(); private final File configFile; - private List customGroups = new ArrayList<>(); + private Map customGroups = new LinkedHashMap<>(); public CustomGroupsConfig(File configDir) { this.configFile = new File(configDir, "customCollapsibleGroups.json"); } public void load() { + customGroups = new LinkedHashMap<>(); if (!configFile.exists()) { - customGroups = new ArrayList<>(); return; } try (Reader reader = new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8)) { List loaded = GSON.fromJson(reader, GROUP_LIST_TYPE); - customGroups = loaded != null ? loaded : new ArrayList<>(); + if (loaded != null) { + for (CustomGroup group : loaded) { + customGroups.put(group.id, group); + } + } } catch (Exception e) { Log.get().error("Failed to load custom collapsible groups from {}", configFile, e); - customGroups = new ArrayList<>(); + customGroups = new LinkedHashMap<>(); } } public void save() { try (Writer writer = new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8)) { - GSON.toJson(customGroups, GROUP_LIST_TYPE, writer); + GSON.toJson(new ArrayList<>(customGroups.values()), GROUP_LIST_TYPE, writer); } catch (Exception e) { Log.get().error("Failed to save custom collapsible groups to {}", configFile, e); } } - public List getCustomGroups() { - return customGroups; + public Collection getCustomGroups() { + return Collections.unmodifiableCollection(customGroups.values()); + } + + @Nullable + public CustomGroup getGroup(String id) { + return customGroups.get(id); } public void addGroup(CustomGroup group) { - customGroups.add(group); + customGroups.put(group.id, group); save(); } public void removeGroup(String id) { - customGroups.removeIf(g -> g.id.equals(id)); + customGroups.remove(id); save(); } public void updateGroup(CustomGroup updated) { - for (int i = 0; i < customGroups.size(); i++) { - if (customGroups.get(i).id.equals(updated.id)) { - customGroups.set(i, updated); - save(); - return; - } - } - // Not found — add as new - addGroup(updated); + customGroups.put(updated.id, updated); + save(); } /** diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 829960873..20c531386 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -259,11 +259,10 @@ protected void actionPerformed(GuiButton button) throws IOException { if (card.source == GroupSource.CUSTOM) { CustomGroupsConfig customGroupsConfig = Config.getCustomGroupsConfig(); if (customGroupsConfig != null) { - for (CustomGroupsConfig.CustomGroup group : customGroupsConfig.getCustomGroups()) { - if (group.id.equals(card.id)) { - this.mc.displayGuiScreen(new GuiCustomGroupEditor(this, group)); - return; - } + CustomGroupsConfig.CustomGroup group = customGroupsConfig.getGroup(card.id); + if (group != null) { + this.mc.displayGuiScreen(new GuiCustomGroupEditor(this, group)); + return; } } } From b86323cedf0c761114ede028a07ef0bf4554cefa Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 17:34:18 -0400 Subject: [PATCH 43/48] CollapsedStack implements IIngredientListElement --- .../gui/overlay/IIngredientGridSource.java | 5 +- .../overlay/IngredientGridWithNavigation.java | 2 +- .../mezz/jei/ingredients/CollapsedStack.java | 86 ++++++++++++++++++- .../CollapsedStackIngredientHelper.java | 2 + .../jei/ingredients/IngredientFilter.java | 12 +-- .../render/IngredientListBatchRenderer.java | 19 ++-- 6 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java index 2c805eee3..25071ce74 100644 --- a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java +++ b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java @@ -12,9 +12,8 @@ public interface IIngredientGridSource { * CollapsedStack objects alongside IIngredientListElement objects * when collapsible groups are enabled. */ - default List getCollapsedIngredientList() { - //noinspection unchecked,rawtypes - return (List) getIngredientList(); + default List getCollapsedIngredientList() { + return getIngredientList(); } /** diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java b/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java index cbb96ec3f..5c8830b7a 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGridWithNavigation.java @@ -51,7 +51,7 @@ public void updateLayout(boolean resetToFirstPage) { if (resetToFirstPage) { firstItemIndex = 0; } - List collapsedList = ingredientSource.getCollapsedIngredientList(); + List collapsedList = ingredientSource.getCollapsedIngredientList(); if (firstItemIndex >= ingredientSource.collapsedSize()) { firstItemIndex = 0; } diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStack.java b/src/main/java/mezz/jei/ingredients/CollapsedStack.java index b880f581e..df9c0beeb 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStack.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStack.java @@ -2,13 +2,17 @@ import mezz.jei.Internal; import mezz.jei.api.ingredients.IIngredientHelper; +import mezz.jei.api.ingredients.IIngredientRenderer; import mezz.jei.api.recipe.IIngredientType; import mezz.jei.gui.ingredients.IIngredientListElement; import net.minecraft.item.ItemStack; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.function.Predicate; /** @@ -20,7 +24,7 @@ * {@link mezz.jei.api.ingredients.IIngredientHelper#translateFocus}. *

*/ -public class CollapsedStack { +public class CollapsedStack implements IIngredientListElement { // Registered as IIngredientType for addon compatibility — addons expect every grid item to have a type public static final IIngredientType TYPE = () -> CollapsedStack.class; @@ -44,6 +48,7 @@ public enum GroupSource { */ @Nullable private Predicate uidMatcher; private boolean expanded; + private boolean visible = true; private final List> ingredients; /** @@ -179,4 +184,83 @@ public int size() { public boolean isEmpty() { return ingredients.isEmpty(); } + + // --- IIngredientListElement implementation --- + + @Override + public CollapsedStack getIngredient() { + return this; + } + + @Override + public int getOrderIndex() { + return ingredients.isEmpty() ? 0 : ingredients.get(0).getOrderIndex(); + } + + @SuppressWarnings("unchecked") + @Override + public IIngredientHelper getIngredientHelper() { + return CollapsedStackIngredientHelper.INSTANCE; + } + + @SuppressWarnings("unchecked") + @Override + public IIngredientRenderer getIngredientRenderer() { + return mezz.jei.render.CollapsedStackRenderer.INSTANCE; + } + + @Override + public String getModNameForSorting() { + return ingredients.isEmpty() ? "" : ingredients.get(0).getModNameForSorting(); + } + + @Override + public Set getModNameStrings() { + return ingredients.isEmpty() ? Collections.emptySet() : ingredients.get(0).getModNameStrings(); + } + + @Override + public List getTooltipStrings() { + return ingredients.isEmpty() ? Collections.emptyList() : ingredients.get(0).getTooltipStrings(); + } + + @Override + public Collection getOreDictStrings() { + return Collections.emptyList(); + } + + @Override + public Collection getCreativeTabsStrings() { + return Collections.emptyList(); + } + + @Override + public Collection getColorStrings() { + return ingredients.isEmpty() ? Collections.emptyList() : ingredients.get(0).getColorStrings(); + } + + @Override + public String getResourceId() { + return "collapsedstack:" + id; + } + + @Override + public boolean isVisible() { + return visible; + } + + @Override + public void setVisible(boolean visible) { + this.visible = visible; + } + + @Override + public int getGroupIndex() { + return 0; + } + + @Override + public boolean startsNewRow() { + return false; + } } diff --git a/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java index a92a7b06e..0416142b4 100644 --- a/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java +++ b/src/main/java/mezz/jei/ingredients/CollapsedStackIngredientHelper.java @@ -17,6 +17,8 @@ * delegate to the first ingredient's helper for addon compatibility. */ public class CollapsedStackIngredientHelper implements IIngredientHelper { + public static final CollapsedStackIngredientHelper INSTANCE = new CollapsedStackIngredientHelper(); + @Nullable @Override public CollapsedStack getMatch(Iterable ingredients, CollapsedStack ingredientToMatch) { diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index bf3886c82..814926ea6 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -37,7 +37,7 @@ public class IngredientFilter implements IIngredientFilter, IIngredientGridSourc private IngredientBlacklistInternal blacklist; private IElementSearch elementSearch; private List ingredientListCached = Collections.emptyList(); - private List collapsedListCached = Collections.emptyList(); + private List collapsedListCached = Collections.emptyList(); @Nullable private String filterCached; /** * Cached sorted list of all currently-visible ingredients — the result of a full @@ -282,16 +282,16 @@ public List getIngredientList(String filterText) { } @Override - public List getCollapsedIngredientList() { + public List getCollapsedIngredientList() { getIngredientList(); // ensure cache is populated return collapsedListCached; } @Override public int collapsedSize() { - List collapsed = getCollapsedIngredientList(); + List collapsed = getCollapsedIngredientList(); int count = 0; - for (Object obj : collapsed) { + for (IIngredientListElement obj : collapsed) { if (obj instanceof CollapsedStack) { CollapsedStack cs = (CollapsedStack) obj; count += cs.isExpanded() ? cs.size() : 1; @@ -410,7 +410,7 @@ private List> withGroupNameMatches( * Each ingredient is assigned to the first matching CollapsedStack group (first match wins). * If collapsible groups are disabled, returns the original list cast to List<Object>. */ - private List collapse(List ingredientList) { + private List collapse(List ingredientList) { if (!Config.isCollapsibleGroupsEnabled()) { return new ArrayList<>(ingredientList); } @@ -457,7 +457,7 @@ private List collapse(List ingredientList) { activeSet.addAll(activeEntries); IdentityHashMap, List> membership = getGroupMembership(); - List result = new ArrayList<>(ingredientList.size()); + List result = new ArrayList<>(ingredientList.size()); Set addedToResult = Collections.newSetFromMap(new IdentityHashMap<>()); for (IIngredientListElement element : ingredientList) { diff --git a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java index 22f76e167..498dfa7b7 100644 --- a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java @@ -138,7 +138,7 @@ public void set(final int startIndex, List ingredientLis * Sets the grid contents from a collapsed ingredient list (mixed IIngredientListElement and CollapsedStack objects). * Collapsed groups are rendered as a single slot; expanded groups have their items rendered individually. */ - public void setCollapsed(final int startIndex, List collapsedList) { + public void setCollapsed(final int startIndex, List collapsedList) { renderItems2d.clear(); renderItems3d.clear(); renderOther.clear(); @@ -158,9 +158,9 @@ public void setCollapsed(final int startIndex, List collapsedList) { // Flatten the ENTIRE collapsed list into display items first, then slice at startIndex. // This ensures expanded groups don't break pagination — firstItemIndex is an index into // the flattened view, which matches what collapsedSize() now returns. - List displayItems = new ArrayList<>(); - Map itemToCollapsed = new HashMap<>(); - for (Object obj : collapsedList) { + List displayItems = new ArrayList<>(); + Map itemToCollapsed = new HashMap<>(); + for (IIngredientListElement obj : collapsedList) { if (obj instanceof CollapsedStack) { CollapsedStack collapsed = (CollapsedStack) obj; if (collapsed.isExpanded()) { @@ -191,7 +191,7 @@ public void setCollapsed(final int startIndex, List collapsedList) { slotIndex++; continue; } - Object displayItem = displayItems.get(i); + IIngredientListElement displayItem = displayItems.get(i); if (displayItem instanceof CollapsedStack) { CollapsedStack collapsed = (CollapsedStack) displayItem; CollapsedStackRenderer renderer = new CollapsedStackRenderer(collapsed); @@ -199,13 +199,12 @@ public void setCollapsed(final int startIndex, List collapsedList) { renderer.setPadding(1); renderCollapsed.add(renderer); collapsedStackIndexed.put(slotIndex, collapsed); - } else if (displayItem instanceof IIngredientListElement) { - IIngredientListElement element = (IIngredientListElement) displayItem; - set(ingredientListSlot, element); - CollapsedStack parentCollapsed = itemToCollapsed.get(element); + } else { + set(ingredientListSlot, displayItem); + CollapsedStack parentCollapsed = itemToCollapsed.get(displayItem); if (parentCollapsed != null) { collapsedStackIndexed.put(slotIndex, parentCollapsed); - expandedElementToGroup.put(element, parentCollapsed); + expandedElementToGroup.put(displayItem, parentCollapsed); expandedGroupSlots.computeIfAbsent(parentCollapsed, k -> new ArrayList<>()) .add(new Rectangle(ingredientListSlot.getArea())); } From 2e388d921afbf74825ab4e133615ad4437ced6c7 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 18:50:54 -0400 Subject: [PATCH 44/48] Fixed code regression Reverted a regression which was fixed in 6467cd6. --- src/main/java/mezz/jei/ingredients/IngredientListElement.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/mezz/jei/ingredients/IngredientListElement.java b/src/main/java/mezz/jei/ingredients/IngredientListElement.java index 9c2cb181c..bce7083c1 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientListElement.java +++ b/src/main/java/mezz/jei/ingredients/IngredientListElement.java @@ -178,8 +178,7 @@ public boolean isVisible() { return true; } if (FMLLaunchHandler.side().isClient()) { - return Config.getShowHiddenIngredientsInCreative() && ProxyCommonClient.isCreative() && - !Internal.getHelpers().getIngredientBlacklist().isIngredientBlacklistedByApi(ingredient); + return Config.getShowHiddenIngredientsInCreative() && ProxyCommonClient.isCreative(); } return false; } From 14e0e3e13acecf8c1bd1969baaa6d4cdb97f5565 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 18:55:05 -0400 Subject: [PATCH 45/48] Revert enchanted book search fix Moving to another PR. --- .../vanilla/ingredients/enchant/EnchantDataHelper.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java b/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java index 389f52b2c..a1c0b8db2 100644 --- a/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java +++ b/src/main/java/mezz/jei/plugins/vanilla/ingredients/enchant/EnchantDataHelper.java @@ -42,10 +42,7 @@ public IFocus translateFocus(IFocus focus, IFocusFactory foc @Override public String getDisplayName(EnchantmentData ingredient) { - // Prefix with the item's localized name so that searching "enchanted" (or "book") - // finds all enchanted-book entries via the NO_PREFIX search tree. - String itemName = new ItemStack(Items.ENCHANTED_BOOK).getDisplayName(); - return itemName + " - " + ingredient.enchantment.getTranslatedName(ingredient.enchantmentLevel); + return ingredient.enchantment.getTranslatedName(ingredient.enchantmentLevel); } @Override From 3058e230757f414f6aa33ea7f9882591a8f1f949 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 18:55:58 -0400 Subject: [PATCH 46/48] Corrected a comment. --- src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java index 25071ce74..a613c0457 100644 --- a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java +++ b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java @@ -17,7 +17,7 @@ default List getCollapsedIngredientList() { } /** - * Returns the total number of display items (counting collapsed groups as 1 each). + * Returns the total number of displayed ingredients (counting collapsed groups as 1 each). */ default int collapsedSize() { return size(); From b61760722c0606347ccad4869e0feba13e85936c Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 21:03:50 -0400 Subject: [PATCH 47/48] Changed JEI lang keys to HEI prefix. Config section needed overrides to make that happen, every other option was changed in place. --- src/main/java/mezz/jei/config/Config.java | 24 ++++++++ .../java/mezz/jei/config/JEIModConfigGui.java | 4 +- .../mezz/jei/gui/overlay/ConfigButton.java | 2 +- .../mezz/jei/gui/overlay/IngredientGrid.java | 2 +- .../collapsible/GuiCollapsibleGroups.java | 20 +++---- .../collapsible/GuiCustomGroupEditor.java | 10 ++-- .../jei/render/CollapsedStackRenderer.java | 4 +- src/main/resources/assets/jei/lang/ar_sa.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/bg_bg.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/cs_cz.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/de_de.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/el_gr.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/en_au.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/en_us.lang | 58 +++++++++---------- src/main/resources/assets/jei/lang/es_es.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/fi_fi.lang | 54 ++++++++--------- src/main/resources/assets/jei/lang/fr_fr.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/he_il.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/it_it.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/ja_jp.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/ko_kr.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/lt_lt.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/nb_no.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/pl_pl.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/pt_br.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/ru_ru.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/sv_se.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/tr_tr.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/uk_ua.lang | 56 +++++++++--------- src/main/resources/assets/jei/lang/zh_cn.lang | 58 ++++++++++--------- src/main/resources/assets/jei/lang/zh_tw.lang | 58 ++++++++++--------- 31 files changed, 721 insertions(+), 693 deletions(-) diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index 966acafb7..31f1b9d74 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -490,6 +490,9 @@ private static boolean syncConfig() { config.addCategory(CATEGORY_ADVANCED); config.addCategory(CATEGORY_MISC); config.addCategory(CATEGORY_COLLAPSIBLE); + // Override collapsible category lang keys from config.jei.* to config.hei.* + config.setCategoryLanguageKey(CATEGORY_COLLAPSIBLE, "config.hei.collapsible"); + config.setCategoryComment(CATEGORY_COLLAPSIBLE, Translator.translateToLocal("config.hei.collapsible.comment")); ConfigCategory modeCategory = config.getCategory("mode"); if (modeCategory != null) { @@ -593,6 +596,27 @@ private static boolean syncConfig() { values.collapsedClickAction = config.getEnum("collapsedClickAction", CATEGORY_COLLAPSIBLE, defaultValues.collapsedClickAction, CollapsedClickAction.values()); + // Override property lang keys and comments from config.jei.collapsible.* to config.hei.collapsible.* + { + String heiPrefix = "config.hei.collapsible."; + + Property collapsibleGroupsEnabledProp = config.get(CATEGORY_COLLAPSIBLE, "collapsibleGroupsEnabled", defaultValues.collapsibleGroupsEnabled); + collapsibleGroupsEnabledProp.setLanguageKey(heiPrefix + "collapsibleGroupsEnabled"); + collapsibleGroupsEnabledProp.setComment(Translator.translateToLocal(heiPrefix + "collapsibleGroupsEnabled.comment")); + + Property collapseOnCloseProp = config.get(CATEGORY_COLLAPSIBLE, "collapseOnClose", defaultValues.collapseOnClose); + collapseOnCloseProp.setLanguageKey(heiPrefix + "collapseOnClose"); + collapseOnCloseProp.setComment(Translator.translateToLocal(heiPrefix + "collapseOnClose.comment")); + + Property collapsedClickActionProp = config.get(CATEGORY_COLLAPSIBLE, "collapsedClickAction", defaultValues.collapsedClickAction.name()); + collapsedClickActionProp.setLanguageKey(heiPrefix + "collapsedClickAction"); + String defaultLocalized = Translator.translateToLocal("config.jei.default"); + String validLocalized = Translator.translateToLocal("config.jei.valid"); + collapsedClickActionProp.setComment(Translator.translateToLocal(heiPrefix + "collapsedClickAction.comment") + + "\n[" + defaultLocalized + ": " + defaultValues.collapsedClickAction.name().toLowerCase(Locale.ENGLISH) + "]" + + "\n[" + validLocalized + ": " + Arrays.toString(collapsedClickActionProp.getValidValues()) + ']'); + } + // Explicit property order so the GUI shows collapsibleGroupsEnabled first, then collapseOnClose. List collapsibleOrder = new ArrayList<>(); collapsibleOrder.add("collapsibleGroupsEnabled"); diff --git a/src/main/java/mezz/jei/config/JEIModConfigGui.java b/src/main/java/mezz/jei/config/JEIModConfigGui.java index 2dd257e4b..d87e3c479 100644 --- a/src/main/java/mezz/jei/config/JEIModConfigGui.java +++ b/src/main/java/mezz/jei/config/JEIModConfigGui.java @@ -149,7 +149,7 @@ public static class ManageGroupsEntry extends GuiConfigEntries.ButtonEntry { public ManageGroupsEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement configElement) { super(owningScreen, owningEntryList, configElement, new GuiButtonExt(0, owningEntryList.controlX, 0, owningEntryList.controlWidth, 18, - I18n.format("jei.gui.collapsible.title"))); + I18n.format("hei.gui.collapsible.title"))); } @Override public void updateValueButtonText() {} @@ -175,7 +175,7 @@ public static class ManageGroupsConfigElement implements IConfigElement { @Override public Class getArrayEntryClass() { return null; } @Override public String getName() { return "manageGroups"; } @Override public String getQualifiedName() { return "manageGroups"; } - @Override public String getLanguageKey() { return "jei.gui.collapsible.title"; } + @Override public String getLanguageKey() { return "hei.gui.collapsible.title"; } @Override public String getComment() { return ""; } @Override public List getChildElements() { return Collections.emptyList(); } @Override public ConfigGuiType getType() { return ConfigGuiType.CONFIG_CATEGORY; } diff --git a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java index d3a8ef836..d397df2dd 100644 --- a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java +++ b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java @@ -37,7 +37,7 @@ private ConfigButton(IDrawable disabledIcon, IDrawable enabledIcon, IngredientLi protected void getTooltips(List tooltip) { tooltip.add(Translator.translateToLocal("jei.tooltip.config")); if (Config.isOverlayEnabled() && Config.isCollapsibleGroupsEnabled()) { - tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.config.expandCollapseAll")); + tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("hei.tooltip.config.expandCollapseAll")); } if (!Config.isOverlayEnabled()) { tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.ingredient.list.disabled")); diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java index e7d51584b..537f191b5 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java @@ -145,7 +145,7 @@ public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { CollapsedStack expandedGroup = guiIngredientSlots.getExpandedCollapsedGroupAt(mouseX, mouseY); if (expandedGroup != null) { String hint = net.minecraft.util.text.TextFormatting.YELLOW - + mezz.jei.util.Translator.translateToLocal("jei.tooltip.collapsed.collapse"); + + mezz.jei.util.Translator.translateToLocal("hei.tooltip.collapsed.collapse"); hovered.drawTooltip(minecraft, mouseX, mouseY, java.util.Collections.singletonList(hint)); } else { hovered.drawTooltip(minecraft, mouseX, mouseY); diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java index 20c531386..633912062 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCollapsibleGroups.java @@ -95,11 +95,11 @@ public void initGui() { // Back button this.buttonList.add(new GuiButton(BTN_BACK, layoutContentLeft, 4, 60, 20, - Translator.translateToLocal("jei.gui.collapsible.back"))); + Translator.translateToLocal("hei.gui.collapsible.back"))); // New Group button this.buttonList.add(new GuiButton(BTN_NEW, layoutContentLeft + layoutContentWidth - 62, 4, 60, 20, - Translator.translateToLocal("jei.gui.collapsible.newGroup"))); + Translator.translateToLocal("hei.gui.collapsible.newGroup"))); rebuildCards(); rebuildPageButtons(); @@ -164,8 +164,8 @@ private void rebuildPageButtons() { // Toggle button String toggleLabel = card.enabled - ? Translator.translateToLocal("jei.gui.collapsible.enabled") - : Translator.translateToLocal("jei.gui.collapsible.disabled"); + ? Translator.translateToLocal("hei.gui.collapsible.enabled") + : Translator.translateToLocal("hei.gui.collapsible.disabled"); this.buttonList.add(new GuiButton(BTN_TOGGLE_BASE + i, btnX, btnY, 52, 20, toggleLabel)); if (card.source == GroupSource.CUSTOM) { @@ -319,7 +319,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.drawDefaultBackground(); // Title - String title = Translator.translateToLocal("jei.gui.collapsible.title"); + String title = Translator.translateToLocal("hei.gui.collapsible.title"); this.drawCenteredString(this.fontRenderer, title, this.width / 2, 10, 0xFFFFFF); int startIdx = currentPage * cardsPerPage; @@ -339,7 +339,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { if (i == pendingDeleteIdx && card.source == GroupSource.CUSTOM) { int btnX = cardX + layoutColWidth - 56; int btnY = cardY + 4; - String confirmLabel = Translator.translateToLocal("jei.gui.collapsible.confirmDelete"); + String confirmLabel = Translator.translateToLocal("hei.gui.collapsible.confirmDelete"); int labelX = btnX - this.fontRenderer.getStringWidth(confirmLabel) - 3; this.fontRenderer.drawStringWithShadow(confirmLabel, labelX, btnY + 27, 0xFFFF4444); } @@ -387,22 +387,22 @@ private void drawCard(GroupCardEntry card, int x, int y, int width, int mouseX, switch (card.source) { case CUSTOM: sourceColor = "\u00A7e"; - sourceLabel = Translator.translateToLocal("jei.gui.collapsible.customGroup"); + sourceLabel = Translator.translateToLocal("hei.gui.collapsible.customGroup"); break; case MOD: sourceColor = "\u00A7d"; - sourceLabel = Translator.translateToLocal("jei.gui.collapsible.modGroup"); + sourceLabel = Translator.translateToLocal("hei.gui.collapsible.modGroup"); break; default: sourceColor = "\u00A77"; - sourceLabel = Translator.translateToLocal("jei.gui.collapsible.defaultGroup"); + sourceLabel = Translator.translateToLocal("hei.gui.collapsible.defaultGroup"); break; } String namePrefix = sourceColor + "[" + sourceLabel + "] \u00A7r"; this.fontRenderer.drawStringWithShadow(namePrefix + card.displayName, x + 4, y + 4, 0xFFFFFF); // Item count - String countText = String.format(Translator.translateToLocal("jei.gui.collapsible.itemCount"), card.itemCount); + String countText = String.format(Translator.translateToLocal("hei.gui.collapsible.itemCount"), card.itemCount); this.fontRenderer.drawStringWithShadow(countText, x + 4, y + 16, 0xAAAAAA); // Scrollable preview grid diff --git a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java index dfdf28178..4e5d4dc46 100644 --- a/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java +++ b/src/main/java/mezz/jei/gui/overlay/collapsible/GuiCustomGroupEditor.java @@ -119,9 +119,9 @@ public void initGui() { // Save & Cancel buttons this.buttonList.add(new GuiButton(BTN_SAVE, panelDivider + 4, 4, 50, 20, - Translator.translateToLocal("jei.gui.collapsible.editor.save"))); + Translator.translateToLocal("hei.gui.collapsible.editor.save"))); this.buttonList.add(new GuiButton(BTN_CANCEL, panelDivider + 58, 4, 50, 20, - Translator.translateToLocal("jei.gui.collapsible.back"))); + Translator.translateToLocal("hei.gui.collapsible.back"))); // Calculate left grid layout int leftWidth = panelDivider - 8; @@ -454,7 +454,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { // Name label this.fontRenderer.drawStringWithShadow( - Translator.translateToLocal("jei.gui.collapsible.editor.name") + ":", + Translator.translateToLocal("hei.gui.collapsible.editor.name") + ":", 4, 10, 0xFFFFFF); if (nameField != null) { nameField.drawTextBox(); @@ -469,9 +469,9 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { drawVerticalLine(panelDivider, 0, this.height, 0xFF555555); // "Selected" header on right panel - String selHeader = Translator.translateToLocal("jei.gui.collapsible.editor.title"); + String selHeader = Translator.translateToLocal("hei.gui.collapsible.editor.title"); this.fontRenderer.drawStringWithShadow(selHeader, panelDivider + 4, 26, 0xCCCCCC); - String selCount = String.format(Translator.translateToLocal("jei.gui.collapsible.editor.selected"), selectedUids.size()); + String selCount = String.format(Translator.translateToLocal("hei.gui.collapsible.editor.selected"), selectedUids.size()); this.fontRenderer.drawStringWithShadow(selCount, panelDivider + 4, 36, 0x888888); // Draw left grid (all items) diff --git a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java index 13cb3bd27..c61954b94 100644 --- a/src/main/java/mezz/jei/render/CollapsedStackRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedStackRenderer.java @@ -216,8 +216,8 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { // In FIRST_ITEM mode, alt+click expands; show that instead. String hint = TextFormatting.YELLOW + Translator.translateToLocal( Config.getCollapsedClickAction() == CollapsedClickAction.OPEN_GROUP - ? "jei.tooltip.collapsed.expand.firstItem" - : "jei.tooltip.collapsed.expand"); + ? "hei.tooltip.collapsed.expand.firstItem" + : "hei.tooltip.collapsed.expand"); int tw = Math.max(Math.max(font.getStringWidth(header), font.getStringWidth(hint)), gridW); int th = 12 + gridH + 10; diff --git a/src/main/resources/assets/jei/lang/ar_sa.lang b/src/main/resources/assets/jei/lang/ar_sa.lang index 189c6fe6e..a6c49b40e 100644 --- a/src/main/resources/assets/jei/lang/ar_sa.lang +++ b/src/main/resources/assets/jei/lang/ar_sa.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=إخفاء زر الإشار config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=ما إذا كان سيتم إخفاء زر الإشارة المرجعية في الزاوية السفلية اليسرى # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+انقر للتوسيع -jei.tooltip.collapsed.expand.firstItem=Alt+انقر للعنصر الأول -jei.tooltip.collapsed.collapse=Alt+انقر للطي -jei.tooltip.config.expandCollapseAll=Alt+النقر لتبديل المجموعات - -config.jei.collapsible=المجموعات القابلة للطي -config.jei.collapsible.comment=خيارات لطي مجموعات المكونات في خانة واحدة في قائمة المكونات. -config.jei.collapsible.collapsibleGroupsEnabled=تفعيل المجموعات القابلة للطي -config.jei.collapsible.collapsibleGroupsEnabled.comment=عند التفعيل، يتم طي مجموعات المكونات ذات الصلة (الجرعات، الكتب المسحورة، إلخ) في خانة واحدة. Alt+انقر على مجموعة لتوسيعها أو طيها. -config.jei.collapsible.collapseOnClose=الطي عند الإغلاق -config.jei.collapsible.collapseOnClose.comment=طي المجموعات المفتوحة حالياً عند إغلاق HEI. -config.jei.collapsible.collapsedClickAction=إجراء النقر الافتراضي -config.jei.collapsible.collapsedClickAction.comment=يبدل إجراء النقرة الواحدة، يصبح الخيار الآخر هو إجراء Alt+النقر. فتح المجموعة يفتح المجموعة. العنصر الأول يطبق الإجراء على أول عنصر في المجموعة. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=فتح المجموعة -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=العنصر الأول +hei.tooltip.collapsed.expand=Alt+انقر للتوسيع +hei.tooltip.collapsed.expand.firstItem=Alt+انقر للعنصر الأول +hei.tooltip.collapsed.collapse=Alt+انقر للطي +hei.tooltip.config.expandCollapseAll=Alt+النقر لتبديل المجموعات + +config.hei.collapsible=المجموعات القابلة للطي +config.hei.collapsible.comment=خيارات لطي مجموعات المكونات في خانة واحدة في قائمة المكونات. +config.hei.collapsible.collapsibleGroupsEnabled=تفعيل المجموعات القابلة للطي +config.hei.collapsible.collapsibleGroupsEnabled.comment=عند التفعيل، يتم طي مجموعات المكونات ذات الصلة (الجرعات، الكتب المسحورة، إلخ) في خانة واحدة. Alt+انقر على مجموعة لتوسيعها أو طيها. +config.hei.collapsible.collapseOnClose=الطي عند الإغلاق +config.hei.collapsible.collapseOnClose.comment=طي المجموعات المفتوحة حالياً عند إغلاق HEI. +config.hei.collapsible.collapsedClickAction=إجراء النقر الافتراضي +config.hei.collapsible.collapsedClickAction.comment=يبدل إجراء النقرة الواحدة، يصبح الخيار الآخر هو إجراء Alt+النقر. فتح المجموعة يفتح المجموعة. العنصر الأول يطبق الإجراء على أول عنصر في المجموعة. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=فتح المجموعة +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=العنصر الأول # Group Management GUI -jei.gui.collapsible.title=إدارة المجموعات -jei.gui.collapsible.back=رجوع -jei.gui.collapsible.newGroup=مجموعة جديدة -jei.gui.collapsible.title.tooltip=تفعيل/تعطيل المجموعات المدمجة وإنشاء مجموعات مخصصة. -jei.gui.collapsible.enabled=مفعّل -jei.gui.collapsible.disabled=معطّل -jei.gui.collapsible.customGroup=مخصص -jei.gui.collapsible.defaultGroup=افتراضي -jei.gui.collapsible.itemCount=%d عناصر -jei.gui.collapsible.editor.title=العناصر المحددة -jei.gui.collapsible.editor.name=الاسم -jei.gui.collapsible.editor.save=حفظ -jei.gui.collapsible.editor.selected=%d محدد \ No newline at end of file +hei.gui.collapsible.title=إدارة المجموعات +hei.gui.collapsible.back=رجوع +hei.gui.collapsible.newGroup=مجموعة جديدة +hei.gui.collapsible.title.tooltip=تفعيل/تعطيل المجموعات المدمجة وإنشاء مجموعات مخصصة. +hei.gui.collapsible.enabled=مفعّل +hei.gui.collapsible.disabled=معطّل +hei.gui.collapsible.customGroup=مخصص +hei.gui.collapsible.defaultGroup=افتراضي +hei.gui.collapsible.itemCount=%d عناصر +hei.gui.collapsible.editor.title=العناصر المحددة +hei.gui.collapsible.editor.name=الاسم +hei.gui.collapsible.editor.save=حفظ +hei.gui.collapsible.editor.selected=%d محدد \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/bg_bg.lang b/src/main/resources/assets/jei/lang/bg_bg.lang index f14361e7c..fe8cf4a4d 100644 --- a/src/main/resources/assets/jei/lang/bg_bg.lang +++ b/src/main/resources/assets/jei/lang/bg_bg.lang @@ -121,34 +121,34 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Скрий бутона за config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Дали да се скрие бутона за отметка в долния ляв ъгъл # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Клик за разгъване -jei.tooltip.collapsed.expand.firstItem=Alt+Клик за първия предмет -jei.tooltip.collapsed.collapse=Alt+Клик за свиване -jei.tooltip.config.expandCollapseAll=Alt+Клик за превключване на групи - -config.jei.collapsible=Свиваеми групи -config.jei.collapsible.comment=Опции за свиване на групи от съставки в един слот в списъка със съставки. -config.jei.collapsible.collapsibleGroupsEnabled=Активиране на свиваеми групи -config.jei.collapsible.collapsibleGroupsEnabled.comment=Когато е активирано, групи от свързани съставки (отвари, омагьосани книги и др.) се свиват в един слот. Alt+Клик върху група, за да я разгънете или свиете. -config.jei.collapsible.collapseOnClose=Свиване при затваряне -config.jei.collapsible.collapseOnClose.comment=Свиване на текущо отворените групи при затваряне на HEI. -config.jei.collapsible.collapsedClickAction=Действие при клик по подразбиране -config.jei.collapsible.collapsedClickAction.comment=Разменя действието при единично кликване, другата опция става действието при Alt+Клик. Отвори групата отваря групата. Първият предмет прилага действието към първия предмет в групата. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Отвори групата -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Първият предмет +hei.tooltip.collapsed.expand=Alt+Клик за разгъване +hei.tooltip.collapsed.expand.firstItem=Alt+Клик за първия предмет +hei.tooltip.collapsed.collapse=Alt+Клик за свиване +hei.tooltip.config.expandCollapseAll=Alt+Клик за превключване на групи + +config.hei.collapsible=Свиваеми групи +config.hei.collapsible.comment=Опции за свиване на групи от съставки в един слот в списъка със съставки. +config.hei.collapsible.collapsibleGroupsEnabled=Активиране на свиваеми групи +config.hei.collapsible.collapsibleGroupsEnabled.comment=Когато е активирано, групи от свързани съставки (отвари, омагьосани книги и др.) се свиват в един слот. Alt+Клик върху група, за да я разгънете или свиете. +config.hei.collapsible.collapseOnClose=Свиване при затваряне +config.hei.collapsible.collapseOnClose.comment=Свиване на текущо отворените групи при затваряне на HEI. +config.hei.collapsible.collapsedClickAction=Действие при клик по подразбиране +config.hei.collapsible.collapsedClickAction.comment=Разменя действието при единично кликване, другата опция става действието при Alt+Клик. Отвори групата отваря групата. Първият предмет прилага действието към първия предмет в групата. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Отвори групата +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Първият предмет # Group Management GUI -jei.gui.collapsible.title=Управление на групи -jei.gui.collapsible.back=Назад -jei.gui.collapsible.newGroup=Нова група -jei.gui.collapsible.title.tooltip=Активиране/деактивиране на вградени групи и създаване на потребителски групи. -jei.gui.collapsible.enabled=Активирано -jei.gui.collapsible.disabled=Деактивирано -jei.gui.collapsible.customGroup=Потребителска -jei.gui.collapsible.defaultGroup=По подразбиране -jei.gui.collapsible.itemCount=%d предмети -jei.gui.collapsible.editor.title=Избрани предмети -jei.gui.collapsible.editor.name=Название -jei.gui.collapsible.editor.save=Запазване -jei.gui.collapsible.editor.selected=%d избрани +hei.gui.collapsible.title=Управление на групи +hei.gui.collapsible.back=Назад +hei.gui.collapsible.newGroup=Нова група +hei.gui.collapsible.title.tooltip=Активиране/деактивиране на вградени групи и създаване на потребителски групи. +hei.gui.collapsible.enabled=Активирано +hei.gui.collapsible.disabled=Деактивирано +hei.gui.collapsible.customGroup=Потребителска +hei.gui.collapsible.defaultGroup=По подразбиране +hei.gui.collapsible.itemCount=%d предмети +hei.gui.collapsible.editor.title=Избрани предмети +hei.gui.collapsible.editor.name=Название +hei.gui.collapsible.editor.save=Запазване +hei.gui.collapsible.editor.selected=%d избрани diff --git a/src/main/resources/assets/jei/lang/cs_cz.lang b/src/main/resources/assets/jei/lang/cs_cz.lang index ea9c50a61..129fa18a0 100644 --- a/src/main/resources/assets/jei/lang/cs_cz.lang +++ b/src/main/resources/assets/jei/lang/cs_cz.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Skrýt tlačítko záložky v config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Zda skrýt tlačítko záložky v levém dolním rohu # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Klik pro rozbalení -jei.tooltip.collapsed.expand.firstItem=Alt+Klik pro první předmět -jei.tooltip.collapsed.collapse=Alt+Klik pro sbalení -jei.tooltip.config.expandCollapseAll=Alt+Klik pro přepínat skupiny - -config.jei.collapsible=Sbalitelné skupiny -config.jei.collapsible.comment=Možnosti pro sbalení skupin ingrediencí do jednoho slotu v seznamu ingrediencí. -config.jei.collapsible.collapsibleGroupsEnabled=Povolit sbalitelné skupiny -config.jei.collapsible.collapsibleGroupsEnabled.comment=Pokud je povoleno, skupiny souvisejících ingrediencí (lektvary, očarované knihy atd.) jsou sbaleny do jednoho slotu. Alt+Klik na skupinu pro rozbalení nebo sbalení. -config.jei.collapsible.collapseOnClose=Sbalit při zavření -config.jei.collapsible.collapseOnClose.comment=Sbalí aktuálně otevřené skupiny při zavření HEI. -config.jei.collapsible.collapsedClickAction=Výchozí akce kliknutí -config.jei.collapsible.collapsedClickAction.comment=Zamění akci jednoduchého kliknutí, druhá možnost se stane akcí Alt+Klik. Otevřít skupinu otevře skupinu. První předmět aplikuje akci na první předmět ve skupině. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Otevřít skupinu -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=První předmět +hei.tooltip.collapsed.expand=Alt+Klik pro rozbalení +hei.tooltip.collapsed.expand.firstItem=Alt+Klik pro první předmět +hei.tooltip.collapsed.collapse=Alt+Klik pro sbalení +hei.tooltip.config.expandCollapseAll=Alt+Klik pro přepínat skupiny + +config.hei.collapsible=Sbalitelné skupiny +config.hei.collapsible.comment=Možnosti pro sbalení skupin ingrediencí do jednoho slotu v seznamu ingrediencí. +config.hei.collapsible.collapsibleGroupsEnabled=Povolit sbalitelné skupiny +config.hei.collapsible.collapsibleGroupsEnabled.comment=Pokud je povoleno, skupiny souvisejících ingrediencí (lektvary, očarované knihy atd.) jsou sbaleny do jednoho slotu. Alt+Klik na skupinu pro rozbalení nebo sbalení. +config.hei.collapsible.collapseOnClose=Sbalit při zavření +config.hei.collapsible.collapseOnClose.comment=Sbalí aktuálně otevřené skupiny při zavření HEI. +config.hei.collapsible.collapsedClickAction=Výchozí akce kliknutí +config.hei.collapsible.collapsedClickAction.comment=Zamění akci jednoduchého kliknutí, druhá možnost se stane akcí Alt+Klik. Otevřít skupinu otevře skupinu. První předmět aplikuje akci na první předmět ve skupině. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Otevřít skupinu +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=První předmět # Group Management GUI -jei.gui.collapsible.title=Správa skupin -jei.gui.collapsible.back=Zpět -jei.gui.collapsible.newGroup=Nová skupina -jei.gui.collapsible.title.tooltip=Povolení/zakázání vestavěných skupin a vytváření uživatelských skupin. -jei.gui.collapsible.enabled=Povoleno -jei.gui.collapsible.disabled=Zakázáno -jei.gui.collapsible.customGroup=Vlastní -jei.gui.collapsible.defaultGroup=Výchozí -jei.gui.collapsible.itemCount=%d předmětů -jei.gui.collapsible.editor.title=Vybrané předměty -jei.gui.collapsible.editor.name=Název -jei.gui.collapsible.editor.save=Uložit -jei.gui.collapsible.editor.selected=%d vybráno +hei.gui.collapsible.title=Správa skupin +hei.gui.collapsible.back=Zpět +hei.gui.collapsible.newGroup=Nová skupina +hei.gui.collapsible.title.tooltip=Povolení/zakázání vestavěných skupin a vytváření uživatelských skupin. +hei.gui.collapsible.enabled=Povoleno +hei.gui.collapsible.disabled=Zakázáno +hei.gui.collapsible.customGroup=Vlastní +hei.gui.collapsible.defaultGroup=Výchozí +hei.gui.collapsible.itemCount=%d předmětů +hei.gui.collapsible.editor.title=Vybrané předměty +hei.gui.collapsible.editor.name=Název +hei.gui.collapsible.editor.save=Uložit +hei.gui.collapsible.editor.selected=%d vybráno diff --git a/src/main/resources/assets/jei/lang/de_de.lang b/src/main/resources/assets/jei/lang/de_de.lang index 375fe6162..ce904480b 100644 --- a/src/main/resources/assets/jei/lang/de_de.lang +++ b/src/main/resources/assets/jei/lang/de_de.lang @@ -154,33 +154,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Lesezeichen-Button in der unt config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ob der Lesezeichen-Button in der unteren linken Ecke ausgeblendet werden soll # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Klick zum Aufklappen -jei.tooltip.collapsed.expand.firstItem=Alt+Klick für erstes Element -jei.tooltip.collapsed.collapse=Alt+Klick zum Zuklappen -jei.tooltip.config.expandCollapseAll=Alt+Klick zum Gruppen umschalten - -config.jei.collapsible=Einklappbare Gruppen -config.jei.collapsible.comment=Optionen zum Einklappen von Zutatengruppen in einen einzelnen Slot in der Zutatenliste. -config.jei.collapsible.collapsibleGroupsEnabled=Einklappbare Gruppen aktivieren -config.jei.collapsible.collapsibleGroupsEnabled.comment=Wenn aktiviert, werden Gruppen verwandter Zutaten (Tränke, verzauberte Bücher usw.) in einen einzelnen Slot zusammengeklappt. Alt+Klick auf eine Gruppe zum Auf- oder Zuklappen. -config.jei.collapsible.collapseOnClose=Beim Schließen einklappen -config.jei.collapsible.collapseOnClose.comment=Aktuell geöffnete Gruppen beim Schließen von HEI einklappen. -config.jei.collapsible.collapsedClickAction=Standard-Klickaktion -config.jei.collapsible.collapsedClickAction.comment=Wechselt die Einzelklick-Aktion, die andere Option wird zur Alt+Klick-Aktion. Gruppe öffnen öffnet die Gruppe. Erstes Element wendet die Aktion auf das erste Element der Gruppe an. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Gruppe öffnen -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Erstes Element +hei.tooltip.collapsed.expand=Alt+Klick zum Aufklappen +hei.tooltip.collapsed.expand.firstItem=Alt+Klick für erstes Element +hei.tooltip.collapsed.collapse=Alt+Klick zum Zuklappen +hei.tooltip.config.expandCollapseAll=Alt+Klick zum Gruppen umschalten + +config.hei.collapsible=Einklappbare Gruppen +config.hei.collapsible.comment=Optionen zum Einklappen von Zutatengruppen in einen einzelnen Slot in der Zutatenliste. +config.hei.collapsible.collapsibleGroupsEnabled=Einklappbare Gruppen aktivieren +config.hei.collapsible.collapsibleGroupsEnabled.comment=Wenn aktiviert, werden Gruppen verwandter Zutaten (Tränke, verzauberte Bücher usw.) in einen einzelnen Slot zusammengeklappt. Alt+Klick auf eine Gruppe zum Auf- oder Zuklappen. +config.hei.collapsible.collapseOnClose=Beim Schließen einklappen +config.hei.collapsible.collapseOnClose.comment=Aktuell geöffnete Gruppen beim Schließen von HEI einklappen. +config.hei.collapsible.collapsedClickAction=Standard-Klickaktion +config.hei.collapsible.collapsedClickAction.comment=Wechselt die Einzelklick-Aktion, die andere Option wird zur Alt+Klick-Aktion. Gruppe öffnen öffnet die Gruppe. Erstes Element wendet die Aktion auf das erste Element der Gruppe an. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Gruppe öffnen +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Erstes Element # Group Management GUI -jei.gui.collapsible.title=Gruppen verwalten -jei.gui.collapsible.back=Zurück -jei.gui.collapsible.newGroup=Neue Gruppe -jei.gui.collapsible.title.tooltip=Integrierte Gruppen aktivieren/deaktivieren und benutzerdefinierte Gruppen erstellen. -jei.gui.collapsible.enabled=Aktiviert -jei.gui.collapsible.disabled=Deaktiviert -jei.gui.collapsible.customGroup=Benutzerdefiniert -jei.gui.collapsible.defaultGroup=Standard -jei.gui.collapsible.itemCount=%d Elemente -jei.gui.collapsible.editor.title=Ausgewählte Elemente -jei.gui.collapsible.editor.name=Name -jei.gui.collapsible.editor.save=Speichern -jei.gui.collapsible.editor.selected=%d ausgewählt +hei.gui.collapsible.title=Gruppen verwalten +hei.gui.collapsible.back=Zurück +hei.gui.collapsible.newGroup=Neue Gruppe +hei.gui.collapsible.title.tooltip=Integrierte Gruppen aktivieren/deaktivieren und benutzerdefinierte Gruppen erstellen. +hei.gui.collapsible.enabled=Aktiviert +hei.gui.collapsible.disabled=Deaktiviert +hei.gui.collapsible.customGroup=Benutzerdefiniert +hei.gui.collapsible.defaultGroup=Standard +hei.gui.collapsible.itemCount=%d Elemente +hei.gui.collapsible.editor.title=Ausgewählte Elemente +hei.gui.collapsible.editor.name=Name +hei.gui.collapsible.editor.save=Speichern +hei.gui.collapsible.editor.selected=%d ausgewählt diff --git a/src/main/resources/assets/jei/lang/el_gr.lang b/src/main/resources/assets/jei/lang/el_gr.lang index 83f678f6b..b0bde75e8 100644 --- a/src/main/resources/assets/jei/lang/el_gr.lang +++ b/src/main/resources/assets/jei/lang/el_gr.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Απόκρυψη Κουμπι config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Αν θα αποκρύπτεται το κουμπί σελιδοδείκτη στην κάτω αριστερή γωνία # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Κλικ για ανάπτυξη -jei.tooltip.collapsed.expand.firstItem=Alt+Κλικ για πρώτο αντικείμενο -jei.tooltip.collapsed.collapse=Alt+Κλικ για σύμπτυξη -jei.tooltip.config.expandCollapseAll=Alt+Κλικ Εναλλαγή Ομάδων - -config.jei.collapsible=Πτυσσόμενες Ομάδες -config.jei.collapsible.comment=Επιλογές για σύμπτυξη ομάδων συστατικών σε μία θέση στη λίστα συστατικών. -config.jei.collapsible.collapsibleGroupsEnabled=Ενεργοποίηση πτυσσόμενων ομάδων -config.jei.collapsible.collapsibleGroupsEnabled.comment=Όταν ενεργοποιηθεί, οι ομάδες σχετικών συστατικών (φίλτρα, μαγεμένα βιβλία κ.λπ.) συμπτύσσονται σε μία θέση. Alt+Κλικ σε μια ομάδα για ανάπτυξη ή σύμπτυξη. -config.jei.collapsible.collapseOnClose=Σύμπτυξη κατά το κλείσιμο -config.jei.collapsible.collapseOnClose.comment=Σύμπτυξη των τρεχόντων ανοιχτών ομάδων όταν κλείνει το HEI. -config.jei.collapsible.collapsedClickAction=Προεπιλεγμένη ενέργεια κλικ -config.jei.collapsible.collapsedClickAction.comment=Εναλλάσσει την ενέργεια μονού κλικ, η άλλη επιλογή γίνεται η ενέργεια Alt+Κλικ. Άνοιγμα Ομάδας ανοίγει την ομάδα. Πρώτο Αντικείμενο εφαρμόζει την ενέργεια στο πρώτο αντικείμενο της ομάδας. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Άνοιγμα Ομάδας -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Πρώτο Αντικείμενο +hei.tooltip.collapsed.expand=Alt+Κλικ για ανάπτυξη +hei.tooltip.collapsed.expand.firstItem=Alt+Κλικ για πρώτο αντικείμενο +hei.tooltip.collapsed.collapse=Alt+Κλικ για σύμπτυξη +hei.tooltip.config.expandCollapseAll=Alt+Κλικ Εναλλαγή Ομάδων + +config.hei.collapsible=Πτυσσόμενες Ομάδες +config.hei.collapsible.comment=Επιλογές για σύμπτυξη ομάδων συστατικών σε μία θέση στη λίστα συστατικών. +config.hei.collapsible.collapsibleGroupsEnabled=Ενεργοποίηση πτυσσόμενων ομάδων +config.hei.collapsible.collapsibleGroupsEnabled.comment=Όταν ενεργοποιηθεί, οι ομάδες σχετικών συστατικών (φίλτρα, μαγεμένα βιβλία κ.λπ.) συμπτύσσονται σε μία θέση. Alt+Κλικ σε μια ομάδα για ανάπτυξη ή σύμπτυξη. +config.hei.collapsible.collapseOnClose=Σύμπτυξη κατά το κλείσιμο +config.hei.collapsible.collapseOnClose.comment=Σύμπτυξη των τρεχόντων ανοιχτών ομάδων όταν κλείνει το HEI. +config.hei.collapsible.collapsedClickAction=Προεπιλεγμένη ενέργεια κλικ +config.hei.collapsible.collapsedClickAction.comment=Εναλλάσσει την ενέργεια μονού κλικ, η άλλη επιλογή γίνεται η ενέργεια Alt+Κλικ. Άνοιγμα Ομάδας ανοίγει την ομάδα. Πρώτο Αντικείμενο εφαρμόζει την ενέργεια στο πρώτο αντικείμενο της ομάδας. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Άνοιγμα Ομάδας +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Πρώτο Αντικείμενο # Group Management GUI -jei.gui.collapsible.title=Διαχείριση ομάδων -jei.gui.collapsible.back=Πίσω -jei.gui.collapsible.newGroup=Νέα ομάδα -jei.gui.collapsible.title.tooltip=Ενεργοποίηση/απενεργοποίηση ενσωματωμένων ομάδων και δημιουργία ομάδων χρήστη. -jei.gui.collapsible.enabled=Ενεργοποιημένο -jei.gui.collapsible.disabled=Απενεργοποιημένο -jei.gui.collapsible.customGroup=Προσαρμοσμένη -jei.gui.collapsible.defaultGroup=Προεπιλογή -jei.gui.collapsible.itemCount=%d αντικείμενα -jei.gui.collapsible.editor.title=Επιλεγμένα αντικείμενα -jei.gui.collapsible.editor.name=Όνομα -jei.gui.collapsible.editor.save=Αποθήκευση -jei.gui.collapsible.editor.selected=%d επιλεγμένα +hei.gui.collapsible.title=Διαχείριση ομάδων +hei.gui.collapsible.back=Πίσω +hei.gui.collapsible.newGroup=Νέα ομάδα +hei.gui.collapsible.title.tooltip=Ενεργοποίηση/απενεργοποίηση ενσωματωμένων ομάδων και δημιουργία ομάδων χρήστη. +hei.gui.collapsible.enabled=Ενεργοποιημένο +hei.gui.collapsible.disabled=Απενεργοποιημένο +hei.gui.collapsible.customGroup=Προσαρμοσμένη +hei.gui.collapsible.defaultGroup=Προεπιλογή +hei.gui.collapsible.itemCount=%d αντικείμενα +hei.gui.collapsible.editor.title=Επιλεγμένα αντικείμενα +hei.gui.collapsible.editor.name=Όνομα +hei.gui.collapsible.editor.save=Αποθήκευση +hei.gui.collapsible.editor.selected=%d επιλεγμένα diff --git a/src/main/resources/assets/jei/lang/en_au.lang b/src/main/resources/assets/jei/lang/en_au.lang index f0c6c8820..169368962 100644 --- a/src/main/resources/assets/jei/lang/en_au.lang +++ b/src/main/resources/assets/jei/lang/en_au.lang @@ -122,33 +122,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Hide Bottom-Left Corner Bookm config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Whether to hide the bottom-left corner bookmark button # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Click to expand -jei.tooltip.collapsed.expand.firstItem=Alt+Click for first item -jei.tooltip.collapsed.collapse=Alt+Click to collapse -jei.tooltip.config.expandCollapseAll=Alt+Click Toggle Groups - -config.jei.collapsible=Collapsible Groups -config.jei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. -config.jei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups -config.jei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. -config.jei.collapsible.collapseOnClose=Collapse on Close -config.jei.collapsible.collapseOnClose.comment=Collapse currently opened groups when HEI is closed. -config.jei.collapsible.collapsedClickAction=Default Collapsed Action -config.jei.collapsible.collapsedClickAction.comment=Swaps the single click action, The other option becomes the Alt+Click action. Open Group opens the group. First Item applies the action to the first item in the group. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Open Group -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=First Item +hei.tooltip.collapsed.expand=Alt+Click to expand +hei.tooltip.collapsed.expand.firstItem=Alt+Click for first item +hei.tooltip.collapsed.collapse=Alt+Click to collapse +hei.tooltip.config.expandCollapseAll=Alt+Click Toggle Groups + +config.hei.collapsible=Collapsible Groups +config.hei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. +config.hei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups +config.hei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. +config.hei.collapsible.collapseOnClose=Collapse on Close +config.hei.collapsible.collapseOnClose.comment=Collapse currently opened groups when HEI is closed. +config.hei.collapsible.collapsedClickAction=Default Collapsed Action +config.hei.collapsible.collapsedClickAction.comment=Swaps the single click action, The other option becomes the Alt+Click action. Open Group opens the group. First Item applies the action to the first item in the group. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Open Group +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=First Item # Group Management GUI -jei.gui.collapsible.title=Manage Groups -jei.gui.collapsible.back=Back -jei.gui.collapsible.newGroup=New Group -jei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user defined groups. -jei.gui.collapsible.enabled=Enabled -jei.gui.collapsible.disabled=Disabled -jei.gui.collapsible.customGroup=Custom -jei.gui.collapsible.defaultGroup=Default -jei.gui.collapsible.itemCount=%d items -jei.gui.collapsible.editor.title=Selected Items -jei.gui.collapsible.editor.name=Name -jei.gui.collapsible.editor.save=Save -jei.gui.collapsible.editor.selected=%d selected +hei.gui.collapsible.title=Manage Groups +hei.gui.collapsible.back=Back +hei.gui.collapsible.newGroup=New Group +hei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user defined groups. +hei.gui.collapsible.enabled=Enabled +hei.gui.collapsible.disabled=Disabled +hei.gui.collapsible.customGroup=Custom +hei.gui.collapsible.defaultGroup=Default +hei.gui.collapsible.itemCount=%d items +hei.gui.collapsible.editor.title=Selected Items +hei.gui.collapsible.editor.name=Name +hei.gui.collapsible.editor.save=Save +hei.gui.collapsible.editor.selected=%d selected diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index be83a97ad..4c6355cac 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -23,10 +23,10 @@ jei.tooltip.bookmarks.usage.key=Hover over an ingredient and press "%s" to bookm jei.tooltip.bookmarks.not.enough.space=There is not enough space to display bookmarks here. # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Click to Expand -jei.tooltip.collapsed.expand.firstItem=Alt+Click for First Item -jei.tooltip.collapsed.collapse=Alt+Click to Collapse -jei.tooltip.config.expandCollapseAll=Alt+Click Toggle Groups +hei.tooltip.collapsed.expand=Alt+Click to Expand +hei.tooltip.collapsed.expand.firstItem=Alt+Click for First Item +hei.tooltip.collapsed.collapse=Alt+Click to Collapse +hei.tooltip.config.expandCollapseAll=Alt+Click Toggle Groups # Error Tooltips jei.tooltip.error.recipe.transfer.missing=Missing Items @@ -188,16 +188,16 @@ config.jei.misc.hideBottomRightCornerConfigButton.comment=Whether to hide the bo config.jei.misc.hideBottomLeftCornerBookmarkButton=Hide Bottom-Left Corner Bookmark Button config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Whether to hide the bottom-left corner bookmark button -config.jei.collapsible=Collapsible Groups -config.jei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. -config.jei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups -config.jei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. -config.jei.collapsible.collapseOnClose=Collapse on Close -config.jei.collapsible.collapseOnClose.comment=Collapse currently opened groups when HEI is closed. -config.jei.collapsible.collapsedClickAction=Default Collapsed Action -config.jei.collapsible.collapsedClickAction.comment=Swaps the single click action, The other option becomes the Alt+Click action. Open Group opens the group. First Item applies the action to the first item in the group. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Open Group -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=First Item +config.hei.collapsible=Collapsible Groups +config.hei.collapsible.comment=Options for collapsing groups of ingredients into a single slot in the ingredient list. +config.hei.collapsible.collapsibleGroupsEnabled=Enable Collapsible Groups +config.hei.collapsible.collapsibleGroupsEnabled.comment=When enabled, groups of related ingredients (potions, enchanted books, etc.) are collapsed into a single slot. Alt+Click a group to expand or collapse it. +config.hei.collapsible.collapseOnClose=Collapse on Close +config.hei.collapsible.collapseOnClose.comment=Collapse currently opened groups when HEI is closed. +config.hei.collapsible.collapsedClickAction=Default Collapsed Action +config.hei.collapsible.collapsedClickAction.comment=Swaps the single click action, The other option becomes the Alt+Click action. Open Group opens the group. First Item applies the action to the first item in the group. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Open Group +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=First Item config.jei.category.categoryUidOrder=Category UID Order config.jei.category.categoryUidOrder.comment=Determines the display order of recipe categories in HEI. Categories listed earlier appear first in the HEI interface. @@ -220,19 +220,19 @@ hei.tooltip.organizer.2=§6UP§r or §6DOWN§7: Move Group Up or Down hei.tooltip.organizer.3=§6CTRL + SCROLL§7: Change Final Output hei.tooltip.organizer.4=§6%s§7: Autocraft -jei.gui.collapsible.title=Manage Groups -jei.gui.collapsible.back=Back -jei.gui.collapsible.newGroup=New Group -jei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user defined groups. -jei.gui.collapsible.enabled=Enabled -jei.gui.collapsible.disabled=Disabled -jei.gui.collapsible.customGroup=Custom -jei.gui.collapsible.modGroup=Mod -jei.gui.collapsible.defaultGroup=Default -jei.gui.collapsible.itemCount=%d items -jei.gui.collapsible.editor.title=Selected Items -jei.gui.collapsible.editor.name=Name -jei.gui.collapsible.editor.save=Save -jei.gui.collapsible.editor.selected=%d selected -jei.gui.collapsible.confirmDelete=Delete? +hei.gui.collapsible.title=Manage Groups +hei.gui.collapsible.back=Back +hei.gui.collapsible.newGroup=New Group +hei.gui.collapsible.title.tooltip=Enable/Disable built in groups and create user defined groups. +hei.gui.collapsible.enabled=Enabled +hei.gui.collapsible.disabled=Disabled +hei.gui.collapsible.customGroup=Custom +hei.gui.collapsible.modGroup=Mod +hei.gui.collapsible.defaultGroup=Default +hei.gui.collapsible.itemCount=%d items +hei.gui.collapsible.editor.title=Selected Items +hei.gui.collapsible.editor.name=Name +hei.gui.collapsible.editor.save=Save +hei.gui.collapsible.editor.selected=%d selected +hei.gui.collapsible.confirmDelete=Delete? hei.tooltip.missing_ingredients=§cMissing Ingredients: \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/es_es.lang b/src/main/resources/assets/jei/lang/es_es.lang index 6990afdbb..6d52595b8 100644 --- a/src/main/resources/assets/jei/lang/es_es.lang +++ b/src/main/resources/assets/jei/lang/es_es.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Ocultar botón de marcador en config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si se debe ocultar el botón de marcador en la esquina inferior izquierda # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Clic para expandir -jei.tooltip.collapsed.expand.firstItem=Alt+Clic para primer objeto -jei.tooltip.collapsed.collapse=Alt+Clic para colapsar -jei.tooltip.config.expandCollapseAll=Alt+Clic alternar grupos - -config.jei.collapsible=Grupos colapsables -config.jei.collapsible.comment=Opciones para colapsar grupos de ingredientes en una sola ranura en la lista de ingredientes. -config.jei.collapsible.collapsibleGroupsEnabled=Habilitar grupos colapsables -config.jei.collapsible.collapsibleGroupsEnabled.comment=Cuando está habilitado, los grupos de ingredientes relacionados (pociones, libros encantados, etc.) se colapsan en una sola ranura. Alt+Clic en un grupo para expandirlo o colapsarlo. -config.jei.collapsible.collapseOnClose=Colapsar al cerrar -config.jei.collapsible.collapseOnClose.comment=Colapsa los grupos actualmente abiertos cuando se cierra HEI. -config.jei.collapsible.collapsedClickAction=Acción de clic predeterminada -config.jei.collapsible.collapsedClickAction.comment=Intercambia la acción del clic simple, la otra opción se convierte en la acción de Alt+Clic. Abrir Grupo abre el grupo. Primer Objeto aplica la acción al primer objeto del grupo. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Abrir Grupo -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Primer Objeto +hei.tooltip.collapsed.expand=Alt+Clic para expandir +hei.tooltip.collapsed.expand.firstItem=Alt+Clic para primer objeto +hei.tooltip.collapsed.collapse=Alt+Clic para colapsar +hei.tooltip.config.expandCollapseAll=Alt+Clic alternar grupos + +config.hei.collapsible=Grupos colapsables +config.hei.collapsible.comment=Opciones para colapsar grupos de ingredientes en una sola ranura en la lista de ingredientes. +config.hei.collapsible.collapsibleGroupsEnabled=Habilitar grupos colapsables +config.hei.collapsible.collapsibleGroupsEnabled.comment=Cuando está habilitado, los grupos de ingredientes relacionados (pociones, libros encantados, etc.) se colapsan en una sola ranura. Alt+Clic en un grupo para expandirlo o colapsarlo. +config.hei.collapsible.collapseOnClose=Colapsar al cerrar +config.hei.collapsible.collapseOnClose.comment=Colapsa los grupos actualmente abiertos cuando se cierra HEI. +config.hei.collapsible.collapsedClickAction=Acción de clic predeterminada +config.hei.collapsible.collapsedClickAction.comment=Intercambia la acción del clic simple, la otra opción se convierte en la acción de Alt+Clic. Abrir Grupo abre el grupo. Primer Objeto aplica la acción al primer objeto del grupo. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Abrir Grupo +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Primer Objeto # Group Management GUI -jei.gui.collapsible.title=Gestionar grupos -jei.gui.collapsible.back=Atrás -jei.gui.collapsible.newGroup=Nuevo grupo -jei.gui.collapsible.title.tooltip=Activar/desactivar grupos integrados y crear grupos definidos por el usuario. -jei.gui.collapsible.enabled=Activado -jei.gui.collapsible.disabled=Desactivado -jei.gui.collapsible.customGroup=Personalizado -jei.gui.collapsible.defaultGroup=Predeterminado -jei.gui.collapsible.itemCount=%d objetos -jei.gui.collapsible.editor.title=Objetos seleccionados -jei.gui.collapsible.editor.name=Nombre -jei.gui.collapsible.editor.save=Guardar -jei.gui.collapsible.editor.selected=%d seleccionados +hei.gui.collapsible.title=Gestionar grupos +hei.gui.collapsible.back=Atrás +hei.gui.collapsible.newGroup=Nuevo grupo +hei.gui.collapsible.title.tooltip=Activar/desactivar grupos integrados y crear grupos definidos por el usuario. +hei.gui.collapsible.enabled=Activado +hei.gui.collapsible.disabled=Desactivado +hei.gui.collapsible.customGroup=Personalizado +hei.gui.collapsible.defaultGroup=Predeterminado +hei.gui.collapsible.itemCount=%d objetos +hei.gui.collapsible.editor.title=Objetos seleccionados +hei.gui.collapsible.editor.name=Nombre +hei.gui.collapsible.editor.save=Guardar +hei.gui.collapsible.editor.selected=%d seleccionados diff --git a/src/main/resources/assets/jei/lang/fi_fi.lang b/src/main/resources/assets/jei/lang/fi_fi.lang index 2c52ef58c..271b09a2c 100644 --- a/src/main/resources/assets/jei/lang/fi_fi.lang +++ b/src/main/resources/assets/jei/lang/fi_fi.lang @@ -121,32 +121,32 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Piilota vasemman alan kulman config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Piilotetaanko vasemman alan kulman kirjanmerkkipainike # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Klikkaa laajentaaksesi -jei.tooltip.collapsed.expand.firstItem=Alt+Klikkaa ensimmäiselle esineelle -jei.tooltip.collapsed.collapse=Alt+Klikkaa tiivistääksesi -jei.tooltip.config.expandCollapseAll=Alt+Klikkaa vaihda ryhmiä -config.jei.collapsible=Tiivistetyt ryhmät -config.jei.collapsible.comment=Asetukset ainesosaryhmien tiivistämiseen yhdeksi paikaksi ainesosaluettelossa. -config.jei.collapsible.collapsibleGroupsEnabled=Ota tiivistetyt ryhmät käyttöön -config.jei.collapsible.collapsibleGroupsEnabled.comment=Kun käytössä, toisiinsa liittyvien ainesosien ryhmät (juomat, lumotut kirjat jne.) tiivistetään yhteen paikkaan. Alt+Klikkaa ryhmää laajentaaksesi tai tiivistääksesi sen. -config.jei.collapsible.collapseOnClose=Tiivistä suljettaessa -config.jei.collapsible.collapseOnClose.comment=Tiivistetään avoinna olevat ryhmät kun HEI suljetaan. -config.jei.collapsible.collapsedClickAction=Oletusklikkitoiminto -config.jei.collapsible.collapsedClickAction.comment=Vaihtaa yksittäisklikkauksen toiminnon, toinen vaihtoehto tulee Alt+Klikkaus-toiminnoksi. Avaa ryhmä avaa ryhmän. Ensimmäinen esine soveltaa toiminnon ryhmän ensimmäiseen esineeseen. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Avaa ryhmä -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Ensimmäinen esine +hei.tooltip.collapsed.expand=Alt+Klikkaa laajentaaksesi +hei.tooltip.collapsed.expand.firstItem=Alt+Klikkaa ensimmäiselle esineelle +hei.tooltip.collapsed.collapse=Alt+Klikkaa tiivistääksesi +hei.tooltip.config.expandCollapseAll=Alt+Klikkaa vaihda ryhmiä +config.hei.collapsible=Tiivistetyt ryhmät +config.hei.collapsible.comment=Asetukset ainesosaryhmien tiivistämiseen yhdeksi paikaksi ainesosaluettelossa. +config.hei.collapsible.collapsibleGroupsEnabled=Ota tiivistetyt ryhmät käyttöön +config.hei.collapsible.collapsibleGroupsEnabled.comment=Kun käytössä, toisiinsa liittyvien ainesosien ryhmät (juomat, lumotut kirjat jne.) tiivistetään yhteen paikkaan. Alt+Klikkaa ryhmää laajentaaksesi tai tiivistääksesi sen. +config.hei.collapsible.collapseOnClose=Tiivistä suljettaessa +config.hei.collapsible.collapseOnClose.comment=Tiivistetään avoinna olevat ryhmät kun HEI suljetaan. +config.hei.collapsible.collapsedClickAction=Oletusklikkitoiminto +config.hei.collapsible.collapsedClickAction.comment=Vaihtaa yksittäisklikkauksen toiminnon, toinen vaihtoehto tulee Alt+Klikkaus-toiminnoksi. Avaa ryhmä avaa ryhmän. Ensimmäinen esine soveltaa toiminnon ryhmän ensimmäiseen esineeseen. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Avaa ryhmä +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Ensimmäinen esine # Group Management GUI -jei.gui.collapsible.title=Hallitse ryhmiä -jei.gui.collapsible.back=Takaisin -jei.gui.collapsible.newGroup=Uusi ryhmä -jei.gui.collapsible.title.tooltip=Sisäänrakennettujen ryhmien käyttöönotto/poistaminen käytöstä ja käyttäjän määrittämien ryhmien luominen. -jei.gui.collapsible.enabled=Käytössä -jei.gui.collapsible.disabled=Ei käytössä -jei.gui.collapsible.customGroup=Mukautettu -jei.gui.collapsible.defaultGroup=Oletus -jei.gui.collapsible.itemCount=%d esinettä -jei.gui.collapsible.editor.title=Valitut esineet -jei.gui.collapsible.editor.name=Nimi -jei.gui.collapsible.editor.save=Tallenna -jei.gui.collapsible.editor.selected=%d valittu +hei.gui.collapsible.title=Hallitse ryhmiä +hei.gui.collapsible.back=Takaisin +hei.gui.collapsible.newGroup=Uusi ryhmä +hei.gui.collapsible.title.tooltip=Sisäänrakennettujen ryhmien käyttöönotto/poistaminen käytöstä ja käyttäjän määrittämien ryhmien luominen. +hei.gui.collapsible.enabled=Käytössä +hei.gui.collapsible.disabled=Ei käytössä +hei.gui.collapsible.customGroup=Mukautettu +hei.gui.collapsible.defaultGroup=Oletus +hei.gui.collapsible.itemCount=%d esinettä +hei.gui.collapsible.editor.title=Valitut esineet +hei.gui.collapsible.editor.name=Nimi +hei.gui.collapsible.editor.save=Tallenna +hei.gui.collapsible.editor.selected=%d valittu diff --git a/src/main/resources/assets/jei/lang/fr_fr.lang b/src/main/resources/assets/jei/lang/fr_fr.lang index 0258778eb..350a79645 100644 --- a/src/main/resources/assets/jei/lang/fr_fr.lang +++ b/src/main/resources/assets/jei/lang/fr_fr.lang @@ -131,33 +131,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Masquer le bouton de marque-p config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Si le bouton de marque-page en bas à gauche doit être masqué # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Clic pour développer -jei.tooltip.collapsed.expand.firstItem=Alt+Clic pour le premier objet -jei.tooltip.collapsed.collapse=Alt+Clic pour réduire -jei.tooltip.config.expandCollapseAll=Alt+Clic basculer les groupes - -config.jei.collapsible=Groupes réductibles -config.jei.collapsible.comment=Options pour réduire les groupes d'ingrédients en un seul emplacement dans la liste des ingrédients. -config.jei.collapsible.collapsibleGroupsEnabled=Activer les groupes réductibles -config.jei.collapsible.collapsibleGroupsEnabled.comment=Lorsqu'activé, les groupes d'ingrédients liés (potions, livres enchantés, etc.) sont réduits en un seul emplacement. Alt+Clic sur un groupe pour le développer ou le réduire. -config.jei.collapsible.collapseOnClose=Réduire à la fermeture -config.jei.collapsible.collapseOnClose.comment=Réduit les groupes actuellement ouverts lorsque HEI est fermé. -config.jei.collapsible.collapsedClickAction=Action de clic par défaut -config.jei.collapsible.collapsedClickAction.comment=Échange l'action du clic simple, l'autre option devient l'action Alt+Clic. Ouvrir le groupe ouvre le groupe. Premier objet applique l'action au premier objet du groupe. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Ouvrir le groupe -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Premier objet +hei.tooltip.collapsed.expand=Alt+Clic pour développer +hei.tooltip.collapsed.expand.firstItem=Alt+Clic pour le premier objet +hei.tooltip.collapsed.collapse=Alt+Clic pour réduire +hei.tooltip.config.expandCollapseAll=Alt+Clic basculer les groupes + +config.hei.collapsible=Groupes réductibles +config.hei.collapsible.comment=Options pour réduire les groupes d'ingrédients en un seul emplacement dans la liste des ingrédients. +config.hei.collapsible.collapsibleGroupsEnabled=Activer les groupes réductibles +config.hei.collapsible.collapsibleGroupsEnabled.comment=Lorsqu'activé, les groupes d'ingrédients liés (potions, livres enchantés, etc.) sont réduits en un seul emplacement. Alt+Clic sur un groupe pour le développer ou le réduire. +config.hei.collapsible.collapseOnClose=Réduire à la fermeture +config.hei.collapsible.collapseOnClose.comment=Réduit les groupes actuellement ouverts lorsque HEI est fermé. +config.hei.collapsible.collapsedClickAction=Action de clic par défaut +config.hei.collapsible.collapsedClickAction.comment=Échange l'action du clic simple, l'autre option devient l'action Alt+Clic. Ouvrir le groupe ouvre le groupe. Premier objet applique l'action au premier objet du groupe. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Ouvrir le groupe +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Premier objet # Group Management GUI -jei.gui.collapsible.title=Gérer les groupes -jei.gui.collapsible.back=Retour -jei.gui.collapsible.newGroup=Nouveau groupe -jei.gui.collapsible.title.tooltip=Activer/désactiver les groupes intégrés et créer des groupes définis par l'utilisateur. -jei.gui.collapsible.enabled=Activé -jei.gui.collapsible.disabled=Désactivé -jei.gui.collapsible.customGroup=Personnalisé -jei.gui.collapsible.defaultGroup=Par défaut -jei.gui.collapsible.itemCount=%d objets -jei.gui.collapsible.editor.title=Objets sélectionnés -jei.gui.collapsible.editor.name=Nom -jei.gui.collapsible.editor.save=Enregistrer -jei.gui.collapsible.editor.selected=%d sélectionnés +hei.gui.collapsible.title=Gérer les groupes +hei.gui.collapsible.back=Retour +hei.gui.collapsible.newGroup=Nouveau groupe +hei.gui.collapsible.title.tooltip=Activer/désactiver les groupes intégrés et créer des groupes définis par l'utilisateur. +hei.gui.collapsible.enabled=Activé +hei.gui.collapsible.disabled=Désactivé +hei.gui.collapsible.customGroup=Personnalisé +hei.gui.collapsible.defaultGroup=Par défaut +hei.gui.collapsible.itemCount=%d objets +hei.gui.collapsible.editor.title=Objets sélectionnés +hei.gui.collapsible.editor.name=Nom +hei.gui.collapsible.editor.save=Enregistrer +hei.gui.collapsible.editor.selected=%d sélectionnés diff --git a/src/main/resources/assets/jei/lang/he_il.lang b/src/main/resources/assets/jei/lang/he_il.lang index 2bbb53f51..76ab104a5 100644 --- a/src/main/resources/assets/jei/lang/he_il.lang +++ b/src/main/resources/assets/jei/lang/he_il.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=הסתר כפתור סימנ config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=האם להסתיר את כפתור הסימנייה בפינה השמאלית התחתונה # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+לחיצה כדי להרחיב -jei.tooltip.collapsed.expand.firstItem=Alt+לחיצה לפריט הראשון -jei.tooltip.collapsed.collapse=Alt+לחיצה כדי לכווץ -jei.tooltip.config.expandCollapseAll=Alt+לחיצה להחלפת קבוצות - -config.jei.collapsible=קבוצות מתכווצות -config.jei.collapsible.comment=אפשרויות לכיווץ קבוצות רכיבים לתא יחיד ברשימת הרכיבים. -config.jei.collapsible.collapsibleGroupsEnabled=הפעל קבוצות מתכווצות -config.jei.collapsible.collapsibleGroupsEnabled.comment=כאשר מופעל, קבוצות של רכיבים קשורים (שיקויים, ספרים מכושפים וכו') מכווצות לתא יחיד. Alt+לחיצה על קבוצה כדי להרחיב או לכווץ אותה. -config.jei.collapsible.collapseOnClose=כיווץ בסגירה -config.jei.collapsible.collapseOnClose.comment=כיווץ קבוצות פתוחות כאשר HEI נסגר. -config.jei.collapsible.collapsedClickAction=פעולת לחיצה ברירת מחדל -config.jei.collapsible.collapsedClickAction.comment=מחליף את פעולת הלחיצה הבודדת, האפשרות השנייה הופכת לפעולת Alt+לחיצה. פתח קבוצה פותח את הקבוצה. פריט ראשון מחיל את הפעולה על הפריט הראשון בקבוצה. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=פתח קבוצה -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=פריט ראשון +hei.tooltip.collapsed.expand=Alt+לחיצה כדי להרחיב +hei.tooltip.collapsed.expand.firstItem=Alt+לחיצה לפריט הראשון +hei.tooltip.collapsed.collapse=Alt+לחיצה כדי לכווץ +hei.tooltip.config.expandCollapseAll=Alt+לחיצה להחלפת קבוצות + +config.hei.collapsible=קבוצות מתכווצות +config.hei.collapsible.comment=אפשרויות לכיווץ קבוצות רכיבים לתא יחיד ברשימת הרכיבים. +config.hei.collapsible.collapsibleGroupsEnabled=הפעל קבוצות מתכווצות +config.hei.collapsible.collapsibleGroupsEnabled.comment=כאשר מופעל, קבוצות של רכיבים קשורים (שיקויים, ספרים מכושפים וכו') מכווצות לתא יחיד. Alt+לחיצה על קבוצה כדי להרחיב או לכווץ אותה. +config.hei.collapsible.collapseOnClose=כיווץ בסגירה +config.hei.collapsible.collapseOnClose.comment=כיווץ קבוצות פתוחות כאשר HEI נסגר. +config.hei.collapsible.collapsedClickAction=פעולת לחיצה ברירת מחדל +config.hei.collapsible.collapsedClickAction.comment=מחליף את פעולת הלחיצה הבודדת, האפשרות השנייה הופכת לפעולת Alt+לחיצה. פתח קבוצה פותח את הקבוצה. פריט ראשון מחיל את הפעולה על הפריט הראשון בקבוצה. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=פתח קבוצה +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=פריט ראשון # Group Management GUI -jei.gui.collapsible.title=ניהול קבוצות -jei.gui.collapsible.back=חזרה -jei.gui.collapsible.newGroup=קבוצה חדשה -jei.gui.collapsible.title.tooltip=הפעלה/השבתה של קבוצות מובנות ויצירת קבוצות מותאמות אישית. -jei.gui.collapsible.enabled=מופעל -jei.gui.collapsible.disabled=מושבת -jei.gui.collapsible.customGroup=מותאם אישית -jei.gui.collapsible.defaultGroup=ברירת מחדל -jei.gui.collapsible.itemCount=%d פריטים -jei.gui.collapsible.editor.title=פריטים נבחרים -jei.gui.collapsible.editor.name=שם -jei.gui.collapsible.editor.save=שמירה -jei.gui.collapsible.editor.selected=%d נבחרו \ No newline at end of file +hei.gui.collapsible.title=ניהול קבוצות +hei.gui.collapsible.back=חזרה +hei.gui.collapsible.newGroup=קבוצה חדשה +hei.gui.collapsible.title.tooltip=הפעלה/השבתה של קבוצות מובנות ויצירת קבוצות מותאמות אישית. +hei.gui.collapsible.enabled=מופעל +hei.gui.collapsible.disabled=מושבת +hei.gui.collapsible.customGroup=מותאם אישית +hei.gui.collapsible.defaultGroup=ברירת מחדל +hei.gui.collapsible.itemCount=%d פריטים +hei.gui.collapsible.editor.title=פריטים נבחרים +hei.gui.collapsible.editor.name=שם +hei.gui.collapsible.editor.save=שמירה +hei.gui.collapsible.editor.selected=%d נבחרו \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/it_it.lang b/src/main/resources/assets/jei/lang/it_it.lang index 9616d727b..bc6a8efb7 100644 --- a/src/main/resources/assets/jei/lang/it_it.lang +++ b/src/main/resources/assets/jei/lang/it_it.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Nascondi pulsante segnalibro config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se nascondere il pulsante segnalibro nell'angolo in basso a sinistra # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Clic per espandere -jei.tooltip.collapsed.expand.firstItem=Alt+Clic per il primo oggetto -jei.tooltip.collapsed.collapse=Alt+Clic per comprimere -jei.tooltip.config.expandCollapseAll=Alt+Clic attiva/disattiva gruppi - -config.jei.collapsible=Gruppi comprimibili -config.jei.collapsible.comment=Opzioni per comprimere gruppi di ingredienti in un singolo slot nell'elenco degli ingredienti. -config.jei.collapsible.collapsibleGroupsEnabled=Abilita gruppi comprimibili -config.jei.collapsible.collapsibleGroupsEnabled.comment=Quando abilitato, i gruppi di ingredienti correlati (pozioni, libri incantati, ecc.) vengono compressi in un singolo slot. Alt+Clic su un gruppo per espanderlo o comprimerlo. -config.jei.collapsible.collapseOnClose=Comprimi alla chiusura -config.jei.collapsible.collapseOnClose.comment=Comprime i gruppi attualmente aperti quando HEI viene chiuso. -config.jei.collapsible.collapsedClickAction=Azione clic predefinita -config.jei.collapsible.collapsedClickAction.comment=Scambia l'azione del clic singolo, l'altra opzione diventa l'azione Alt+Clic. Apri Gruppo apre il gruppo. Primo Oggetto applica l'azione al primo oggetto del gruppo. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Apri Gruppo -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Primo Oggetto +hei.tooltip.collapsed.expand=Alt+Clic per espandere +hei.tooltip.collapsed.expand.firstItem=Alt+Clic per il primo oggetto +hei.tooltip.collapsed.collapse=Alt+Clic per comprimere +hei.tooltip.config.expandCollapseAll=Alt+Clic attiva/disattiva gruppi + +config.hei.collapsible=Gruppi comprimibili +config.hei.collapsible.comment=Opzioni per comprimere gruppi di ingredienti in un singolo slot nell'elenco degli ingredienti. +config.hei.collapsible.collapsibleGroupsEnabled=Abilita gruppi comprimibili +config.hei.collapsible.collapsibleGroupsEnabled.comment=Quando abilitato, i gruppi di ingredienti correlati (pozioni, libri incantati, ecc.) vengono compressi in un singolo slot. Alt+Clic su un gruppo per espanderlo o comprimerlo. +config.hei.collapsible.collapseOnClose=Comprimi alla chiusura +config.hei.collapsible.collapseOnClose.comment=Comprime i gruppi attualmente aperti quando HEI viene chiuso. +config.hei.collapsible.collapsedClickAction=Azione clic predefinita +config.hei.collapsible.collapsedClickAction.comment=Scambia l'azione del clic singolo, l'altra opzione diventa l'azione Alt+Clic. Apri Gruppo apre il gruppo. Primo Oggetto applica l'azione al primo oggetto del gruppo. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Apri Gruppo +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Primo Oggetto # Group Management GUI -jei.gui.collapsible.title=Gestisci gruppi -jei.gui.collapsible.back=Indietro -jei.gui.collapsible.newGroup=Nuovo gruppo -jei.gui.collapsible.title.tooltip=Abilita/disabilita i gruppi integrati e crea gruppi definiti dall'utente. -jei.gui.collapsible.enabled=Abilitato -jei.gui.collapsible.disabled=Disabilitato -jei.gui.collapsible.customGroup=Personalizzato -jei.gui.collapsible.defaultGroup=Predefinito -jei.gui.collapsible.itemCount=%d oggetti -jei.gui.collapsible.editor.title=Oggetti selezionati -jei.gui.collapsible.editor.name=Nome -jei.gui.collapsible.editor.save=Salva -jei.gui.collapsible.editor.selected=%d selezionati \ No newline at end of file +hei.gui.collapsible.title=Gestisci gruppi +hei.gui.collapsible.back=Indietro +hei.gui.collapsible.newGroup=Nuovo gruppo +hei.gui.collapsible.title.tooltip=Abilita/disabilita i gruppi integrati e crea gruppi definiti dall'utente. +hei.gui.collapsible.enabled=Abilitato +hei.gui.collapsible.disabled=Disabilitato +hei.gui.collapsible.customGroup=Personalizzato +hei.gui.collapsible.defaultGroup=Predefinito +hei.gui.collapsible.itemCount=%d oggetti +hei.gui.collapsible.editor.title=Oggetti selezionati +hei.gui.collapsible.editor.name=Nome +hei.gui.collapsible.editor.save=Salva +hei.gui.collapsible.editor.selected=%d selezionati \ No newline at end of file diff --git a/src/main/resources/assets/jei/lang/ja_jp.lang b/src/main/resources/assets/jei/lang/ja_jp.lang index ad5fdcaa2..ba1c1f974 100644 --- a/src/main/resources/assets/jei/lang/ja_jp.lang +++ b/src/main/resources/assets/jei/lang/ja_jp.lang @@ -131,33 +131,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=左下隅のブックマー config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=左下隅のブックマークボタンを非表示にするかどうか # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+クリックで展開 -jei.tooltip.collapsed.expand.firstItem=Alt+クリックで最初のアイテム -jei.tooltip.collapsed.collapse=Alt+クリックで折りたたむ -jei.tooltip.config.expandCollapseAll=Alt+クリックでグループ展開切替 - -config.jei.collapsible=折りたたみグループ -config.jei.collapsible.comment=アイテムリスト内でアイテムグループをひとつのスロットに折りたたむオプション。 -config.jei.collapsible.collapsibleGroupsEnabled=折りたたみグループを有効にする -config.jei.collapsible.collapsibleGroupsEnabled.comment=有効にすると、関連するアイテムのグループ(ポーション、エンチャント本など)がひとつのスロットに折りたたまれます。Alt+クリックでグループを展開または折りたたみます。 -config.jei.collapsible.collapseOnClose=閉じる時に折りたたむ -config.jei.collapsible.collapseOnClose.comment=HEIを閉じた時に現在開いているグループを折りたたみます。 -config.jei.collapsible.collapsedClickAction=デフォルトのクリック動作 -config.jei.collapsible.collapsedClickAction.comment=シングルクリックの動作を切り替えます。もう一方のオプションがAlt+クリックの動作になります。グループを開くはグループを開きます。最初のアイテムはグループの最初のアイテムにアクションを適用します。 -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=グループを開く -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=最初のアイテム +hei.tooltip.collapsed.expand=Alt+クリックで展開 +hei.tooltip.collapsed.expand.firstItem=Alt+クリックで最初のアイテム +hei.tooltip.collapsed.collapse=Alt+クリックで折りたたむ +hei.tooltip.config.expandCollapseAll=Alt+クリックでグループ展開切替 + +config.hei.collapsible=折りたたみグループ +config.hei.collapsible.comment=アイテムリスト内でアイテムグループをひとつのスロットに折りたたむオプション。 +config.hei.collapsible.collapsibleGroupsEnabled=折りたたみグループを有効にする +config.hei.collapsible.collapsibleGroupsEnabled.comment=有効にすると、関連するアイテムのグループ(ポーション、エンチャント本など)がひとつのスロットに折りたたまれます。Alt+クリックでグループを展開または折りたたみます。 +config.hei.collapsible.collapseOnClose=閉じる時に折りたたむ +config.hei.collapsible.collapseOnClose.comment=HEIを閉じた時に現在開いているグループを折りたたみます。 +config.hei.collapsible.collapsedClickAction=デフォルトのクリック動作 +config.hei.collapsible.collapsedClickAction.comment=シングルクリックの動作を切り替えます。もう一方のオプションがAlt+クリックの動作になります。グループを開くはグループを開きます。最初のアイテムはグループの最初のアイテムにアクションを適用します。 +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=グループを開く +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=最初のアイテム # Group Management GUI -jei.gui.collapsible.title=グループ管理 -jei.gui.collapsible.back=戻る -jei.gui.collapsible.newGroup=新しいグループ -jei.gui.collapsible.title.tooltip=組み込みグループを有効/無効にし、ユーザー定義グループを作成します。 -jei.gui.collapsible.enabled=有効 -jei.gui.collapsible.disabled=無効 -jei.gui.collapsible.customGroup=カスタム -jei.gui.collapsible.defaultGroup=デフォルト -jei.gui.collapsible.itemCount=%d アイテム -jei.gui.collapsible.editor.title=選択されたアイテム -jei.gui.collapsible.editor.name=名前 -jei.gui.collapsible.editor.save=保存 -jei.gui.collapsible.editor.selected=%d 選択中 +hei.gui.collapsible.title=グループ管理 +hei.gui.collapsible.back=戻る +hei.gui.collapsible.newGroup=新しいグループ +hei.gui.collapsible.title.tooltip=組み込みグループを有効/無効にし、ユーザー定義グループを作成します。 +hei.gui.collapsible.enabled=有効 +hei.gui.collapsible.disabled=無効 +hei.gui.collapsible.customGroup=カスタム +hei.gui.collapsible.defaultGroup=デフォルト +hei.gui.collapsible.itemCount=%d アイテム +hei.gui.collapsible.editor.title=選択されたアイテム +hei.gui.collapsible.editor.name=名前 +hei.gui.collapsible.editor.save=保存 +hei.gui.collapsible.editor.selected=%d 選択中 diff --git a/src/main/resources/assets/jei/lang/ko_kr.lang b/src/main/resources/assets/jei/lang/ko_kr.lang index d2f5c9c0c..038e59090 100644 --- a/src/main/resources/assets/jei/lang/ko_kr.lang +++ b/src/main/resources/assets/jei/lang/ko_kr.lang @@ -168,33 +168,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=왼쪽 하단 모서리 북 config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=왼쪽 하단 모서리 북마크 버튼을 숨길지 여부 # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+클릭하여 펼치기 -jei.tooltip.collapsed.expand.firstItem=Alt+클릭하여 첫 번째 아이템 -jei.tooltip.collapsed.collapse=Alt+클릭하여 접기 -jei.tooltip.config.expandCollapseAll=Alt+클릭으로 그룹 전환 - -config.jei.collapsible=접을 수 있는 그룹 -config.jei.collapsible.comment=재료 목록에서 재료 그룹을 하나의 슬롯으로 접는 옵션입니다. -config.jei.collapsible.collapsibleGroupsEnabled=접을 수 있는 그룹 활성화 -config.jei.collapsible.collapsibleGroupsEnabled.comment=활성화하면 관련 재료 그룹(물약, 마법이 부여된 책 등)이 하나의 슬롯으로 접힙니다. Alt+클릭으로 그룹을 펼치거나 접을 수 있습니다. -config.jei.collapsible.collapseOnClose=닫을 때 접기 -config.jei.collapsible.collapseOnClose.comment=HEI를 닫을 때 현재 열려 있는 그룹을 접습니다. -config.jei.collapsible.collapsedClickAction=기본 클릭 동작 -config.jei.collapsible.collapsedClickAction.comment=단일 클릭 동작을 전환합니다. 다른 옵션이 Alt+클릭 동작이 됩니다. 그룹 열기는 그룹을 엽니다. 첫 번째 아이템은 그룹의 첫 번째 아이템에 동작을 적용합니다. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=그룹 열기 -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=첫 번째 아이템 +hei.tooltip.collapsed.expand=Alt+클릭하여 펼치기 +hei.tooltip.collapsed.expand.firstItem=Alt+클릭하여 첫 번째 아이템 +hei.tooltip.collapsed.collapse=Alt+클릭하여 접기 +hei.tooltip.config.expandCollapseAll=Alt+클릭으로 그룹 전환 + +config.hei.collapsible=접을 수 있는 그룹 +config.hei.collapsible.comment=재료 목록에서 재료 그룹을 하나의 슬롯으로 접는 옵션입니다. +config.hei.collapsible.collapsibleGroupsEnabled=접을 수 있는 그룹 활성화 +config.hei.collapsible.collapsibleGroupsEnabled.comment=활성화하면 관련 재료 그룹(물약, 마법이 부여된 책 등)이 하나의 슬롯으로 접힙니다. Alt+클릭으로 그룹을 펼치거나 접을 수 있습니다. +config.hei.collapsible.collapseOnClose=닫을 때 접기 +config.hei.collapsible.collapseOnClose.comment=HEI를 닫을 때 현재 열려 있는 그룹을 접습니다. +config.hei.collapsible.collapsedClickAction=기본 클릭 동작 +config.hei.collapsible.collapsedClickAction.comment=단일 클릭 동작을 전환합니다. 다른 옵션이 Alt+클릭 동작이 됩니다. 그룹 열기는 그룹을 엽니다. 첫 번째 아이템은 그룹의 첫 번째 아이템에 동작을 적용합니다. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=그룹 열기 +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=첫 번째 아이템 # Group Management GUI -jei.gui.collapsible.title=그룹 관리 -jei.gui.collapsible.back=뒤로 -jei.gui.collapsible.newGroup=새 그룹 -jei.gui.collapsible.title.tooltip=기본 제공 그룹을 활성화/비활성화하고 사용자 정의 그룹을 만듭니다. -jei.gui.collapsible.enabled=활성화됨 -jei.gui.collapsible.disabled=비활성화됨 -jei.gui.collapsible.customGroup=사용자 정의 -jei.gui.collapsible.defaultGroup=기본값 -jei.gui.collapsible.itemCount=%d 아이템 -jei.gui.collapsible.editor.title=선택된 아이템 -jei.gui.collapsible.editor.name=이름 -jei.gui.collapsible.editor.save=저장 -jei.gui.collapsible.editor.selected=%d 선택됨 +hei.gui.collapsible.title=그룹 관리 +hei.gui.collapsible.back=뒤로 +hei.gui.collapsible.newGroup=새 그룹 +hei.gui.collapsible.title.tooltip=기본 제공 그룹을 활성화/비활성화하고 사용자 정의 그룹을 만듭니다. +hei.gui.collapsible.enabled=활성화됨 +hei.gui.collapsible.disabled=비활성화됨 +hei.gui.collapsible.customGroup=사용자 정의 +hei.gui.collapsible.defaultGroup=기본값 +hei.gui.collapsible.itemCount=%d 아이템 +hei.gui.collapsible.editor.title=선택된 아이템 +hei.gui.collapsible.editor.name=이름 +hei.gui.collapsible.editor.save=저장 +hei.gui.collapsible.editor.selected=%d 선택됨 diff --git a/src/main/resources/assets/jei/lang/lt_lt.lang b/src/main/resources/assets/jei/lang/lt_lt.lang index a71c0f776..e75a5e48c 100644 --- a/src/main/resources/assets/jei/lang/lt_lt.lang +++ b/src/main/resources/assets/jei/lang/lt_lt.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Paslėpti žymės mygtuką ap config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Ar paslėpti žymės mygtuką apatiniame kairiajame kampe # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+spustelėkite, kad išskleistumėte -jei.tooltip.collapsed.expand.firstItem=Alt+spustelėkite pirmam daiktui -jei.tooltip.collapsed.collapse=Alt+spustelėkite, kad sutrauktumėte -jei.tooltip.config.expandCollapseAll=Alt+Spustelėkite perjungti grupes - -config.jei.collapsible=Sutraukiamos grupės -config.jei.collapsible.comment=Parinktys ingredientų grupių sutraukimui į vieną vietą ingredientų sąraše. -config.jei.collapsible.collapsibleGroupsEnabled=Įjungti sutraukiamas grupes -config.jei.collapsible.collapsibleGroupsEnabled.comment=Kai įjungta, susijusių ingredientų grupės (eliksyrai, užkeiktos knygos ir kt.) sutraukiamos į vieną vietą. Alt+spustelėkite grupę, kad ją išskleistumėte ar sutrauktumėte. -config.jei.collapsible.collapseOnClose=Sutraukti uždarant -config.jei.collapsible.collapseOnClose.comment=Sutraukti šiuo metu atidarytas grupes kai HEI uždaromas. -config.jei.collapsible.collapsedClickAction=Numatytasis paspaudimo veiksmas -config.jei.collapsible.collapsedClickAction.comment=Pakeičia vieno paspaudimo veiksmą, kita parinktis tampa Alt+spustelėjimo veiksmu. Atidaryti grupę atidaro grupę. Pirmas daiktas taiko veiksmą pirmam grupės daiktui. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Atidaryti grupę -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Pirmas daiktas +hei.tooltip.collapsed.expand=Alt+spustelėkite, kad išskleistumėte +hei.tooltip.collapsed.expand.firstItem=Alt+spustelėkite pirmam daiktui +hei.tooltip.collapsed.collapse=Alt+spustelėkite, kad sutrauktumėte +hei.tooltip.config.expandCollapseAll=Alt+Spustelėkite perjungti grupes + +config.hei.collapsible=Sutraukiamos grupės +config.hei.collapsible.comment=Parinktys ingredientų grupių sutraukimui į vieną vietą ingredientų sąraše. +config.hei.collapsible.collapsibleGroupsEnabled=Įjungti sutraukiamas grupes +config.hei.collapsible.collapsibleGroupsEnabled.comment=Kai įjungta, susijusių ingredientų grupės (eliksyrai, užkeiktos knygos ir kt.) sutraukiamos į vieną vietą. Alt+spustelėkite grupę, kad ją išskleistumėte ar sutrauktumėte. +config.hei.collapsible.collapseOnClose=Sutraukti uždarant +config.hei.collapsible.collapseOnClose.comment=Sutraukti šiuo metu atidarytas grupes kai HEI uždaromas. +config.hei.collapsible.collapsedClickAction=Numatytasis paspaudimo veiksmas +config.hei.collapsible.collapsedClickAction.comment=Pakeičia vieno paspaudimo veiksmą, kita parinktis tampa Alt+spustelėjimo veiksmu. Atidaryti grupę atidaro grupę. Pirmas daiktas taiko veiksmą pirmam grupės daiktui. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Atidaryti grupę +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Pirmas daiktas # Group Management GUI -jei.gui.collapsible.title=Valdyti grupes -jei.gui.collapsible.back=Atgal -jei.gui.collapsible.newGroup=Nauja grupė -jei.gui.collapsible.title.tooltip=Įgąlinti/išjungti įtaisytąsias grupes ir kurti naudotojo apibrėžtas grupes. -jei.gui.collapsible.enabled=Įgąlinta -jei.gui.collapsible.disabled=Išjungta -jei.gui.collapsible.customGroup=Pasirinktiniű -jei.gui.collapsible.defaultGroup=Numatytoji -jei.gui.collapsible.itemCount=%d daiktų -jei.gui.collapsible.editor.title=Pasirinkti daiktai -jei.gui.collapsible.editor.name=Pavadinimas -jei.gui.collapsible.editor.save=Išsaugoti -jei.gui.collapsible.editor.selected=%d pasirinkta +hei.gui.collapsible.title=Valdyti grupes +hei.gui.collapsible.back=Atgal +hei.gui.collapsible.newGroup=Nauja grupė +hei.gui.collapsible.title.tooltip=Įgąlinti/išjungti įtaisytąsias grupes ir kurti naudotojo apibrėžtas grupes. +hei.gui.collapsible.enabled=Įgąlinta +hei.gui.collapsible.disabled=Išjungta +hei.gui.collapsible.customGroup=Pasirinktiniű +hei.gui.collapsible.defaultGroup=Numatytoji +hei.gui.collapsible.itemCount=%d daiktų +hei.gui.collapsible.editor.title=Pasirinkti daiktai +hei.gui.collapsible.editor.name=Pavadinimas +hei.gui.collapsible.editor.save=Išsaugoti +hei.gui.collapsible.editor.selected=%d pasirinkta diff --git a/src/main/resources/assets/jei/lang/nb_no.lang b/src/main/resources/assets/jei/lang/nb_no.lang index d21a1df35..14bca6704 100644 --- a/src/main/resources/assets/jei/lang/nb_no.lang +++ b/src/main/resources/assets/jei/lang/nb_no.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Skjul bokmerke-knapp i nedre config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmerke-knappen i nedre venstre hjørne skal skjules # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Klikk for å utvide -jei.tooltip.collapsed.expand.firstItem=Alt+Klikk for første gjenstand -jei.tooltip.collapsed.collapse=Alt+Klikk for å skjule -jei.tooltip.config.expandCollapseAll=Alt+Klikk for å bytte grupper - -config.jei.collapsible=Sammenleggbare grupper -config.jei.collapsible.comment=Alternativer for å slå sammen grupper av ingredienser til én plass i ingredienslisten. -config.jei.collapsible.collapsibleGroupsEnabled=Aktiver sammenleggbare grupper -config.jei.collapsible.collapsibleGroupsEnabled.comment=Når aktivert, blir grupper av relaterte ingredienser (drikkevarer, forheksede bøker osv.) slått sammen til én plass. Alt+Klikk på en gruppe for å utvide eller skjule den. -config.jei.collapsible.collapseOnClose=Legg sammen ved lukking -config.jei.collapsible.collapseOnClose.comment=Legger sammen åpne grupper når HEI lukkes. -config.jei.collapsible.collapsedClickAction=Standard klikkhandling -config.jei.collapsible.collapsedClickAction.comment=Bytter enkeltklikkhandlingen, det andre alternativet blir Alt+Klikk-handlingen. Åpne gruppe åpner gruppen. Første gjenstand bruker handlingen på den første gjenstanden i gruppen. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Åpne gruppe -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Første gjenstand +hei.tooltip.collapsed.expand=Alt+Klikk for å utvide +hei.tooltip.collapsed.expand.firstItem=Alt+Klikk for første gjenstand +hei.tooltip.collapsed.collapse=Alt+Klikk for å skjule +hei.tooltip.config.expandCollapseAll=Alt+Klikk for å bytte grupper + +config.hei.collapsible=Sammenleggbare grupper +config.hei.collapsible.comment=Alternativer for å slå sammen grupper av ingredienser til én plass i ingredienslisten. +config.hei.collapsible.collapsibleGroupsEnabled=Aktiver sammenleggbare grupper +config.hei.collapsible.collapsibleGroupsEnabled.comment=Når aktivert, blir grupper av relaterte ingredienser (drikkevarer, forheksede bøker osv.) slått sammen til én plass. Alt+Klikk på en gruppe for å utvide eller skjule den. +config.hei.collapsible.collapseOnClose=Legg sammen ved lukking +config.hei.collapsible.collapseOnClose.comment=Legger sammen åpne grupper når HEI lukkes. +config.hei.collapsible.collapsedClickAction=Standard klikkhandling +config.hei.collapsible.collapsedClickAction.comment=Bytter enkeltklikkhandlingen, det andre alternativet blir Alt+Klikk-handlingen. Åpne gruppe åpner gruppen. Første gjenstand bruker handlingen på den første gjenstanden i gruppen. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Åpne gruppe +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Første gjenstand # Group Management GUI -jei.gui.collapsible.title=Administrer grupper -jei.gui.collapsible.back=Tilbake -jei.gui.collapsible.newGroup=Ny gruppe -jei.gui.collapsible.title.tooltip=Aktiver/deaktiver innebygde grupper og opprett brukerdefinerte grupper. -jei.gui.collapsible.enabled=Aktivert -jei.gui.collapsible.disabled=Deaktivert -jei.gui.collapsible.customGroup=Egendefinert -jei.gui.collapsible.defaultGroup=Standard -jei.gui.collapsible.itemCount=%d gjenstander -jei.gui.collapsible.editor.title=Valgte gjenstander -jei.gui.collapsible.editor.name=Navn -jei.gui.collapsible.editor.save=Lagre -jei.gui.collapsible.editor.selected=%d valgt +hei.gui.collapsible.title=Administrer grupper +hei.gui.collapsible.back=Tilbake +hei.gui.collapsible.newGroup=Ny gruppe +hei.gui.collapsible.title.tooltip=Aktiver/deaktiver innebygde grupper og opprett brukerdefinerte grupper. +hei.gui.collapsible.enabled=Aktivert +hei.gui.collapsible.disabled=Deaktivert +hei.gui.collapsible.customGroup=Egendefinert +hei.gui.collapsible.defaultGroup=Standard +hei.gui.collapsible.itemCount=%d gjenstander +hei.gui.collapsible.editor.title=Valgte gjenstander +hei.gui.collapsible.editor.name=Navn +hei.gui.collapsible.editor.save=Lagre +hei.gui.collapsible.editor.selected=%d valgt diff --git a/src/main/resources/assets/jei/lang/pl_pl.lang b/src/main/resources/assets/jei/lang/pl_pl.lang index b515f7cfa..fa7bb10f0 100644 --- a/src/main/resources/assets/jei/lang/pl_pl.lang +++ b/src/main/resources/assets/jei/lang/pl_pl.lang @@ -134,33 +134,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Ukryj przycisk zakładki w le config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Czy ukryć przycisk zakładki w lewym dolnym rogu # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Klik aby rozwinąć -jei.tooltip.collapsed.expand.firstItem=Alt+Klik dla pierwszego przedmiotu -jei.tooltip.collapsed.collapse=Alt+Klik aby zwinąć -jei.tooltip.config.expandCollapseAll=Alt+Klik aby przełączyć grupy - -config.jei.collapsible=Zwijane grupy -config.jei.collapsible.comment=Opcje zwijania grup składników do jednego slotu na liście składników. -config.jei.collapsible.collapsibleGroupsEnabled=Włącz zwijane grupy -config.jei.collapsible.collapsibleGroupsEnabled.comment=Po włączeniu grupy powiązanych składników (mikstury, zaklęte księgi itp.) są zwijane do jednego slotu. Alt+Klik na grupę, aby ją rozwinąć lub zwinąć. -config.jei.collapsible.collapseOnClose=Zwiń przy zamknięciu -config.jei.collapsible.collapseOnClose.comment=Zwija aktualnie otwarte grupy po zamknięciu HEI. -config.jei.collapsible.collapsedClickAction=Domyślna akcja kliknięcia -config.jei.collapsible.collapsedClickAction.comment=Zamienia akcję pojedynczego kliknięcia, druga opcja staje się akcją Alt+Klik. Otwórz grupę otwiera grupę. Pierwszy przedmiot stosuje akcję do pierwszego przedmiotu w grupie. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Otwórz grupę -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Pierwszy przedmiot +hei.tooltip.collapsed.expand=Alt+Klik aby rozwinąć +hei.tooltip.collapsed.expand.firstItem=Alt+Klik dla pierwszego przedmiotu +hei.tooltip.collapsed.collapse=Alt+Klik aby zwinąć +hei.tooltip.config.expandCollapseAll=Alt+Klik aby przełączyć grupy + +config.hei.collapsible=Zwijane grupy +config.hei.collapsible.comment=Opcje zwijania grup składników do jednego slotu na liście składników. +config.hei.collapsible.collapsibleGroupsEnabled=Włącz zwijane grupy +config.hei.collapsible.collapsibleGroupsEnabled.comment=Po włączeniu grupy powiązanych składników (mikstury, zaklęte księgi itp.) są zwijane do jednego slotu. Alt+Klik na grupę, aby ją rozwinąć lub zwinąć. +config.hei.collapsible.collapseOnClose=Zwiń przy zamknięciu +config.hei.collapsible.collapseOnClose.comment=Zwija aktualnie otwarte grupy po zamknięciu HEI. +config.hei.collapsible.collapsedClickAction=Domyślna akcja kliknięcia +config.hei.collapsible.collapsedClickAction.comment=Zamienia akcję pojedynczego kliknięcia, druga opcja staje się akcją Alt+Klik. Otwórz grupę otwiera grupę. Pierwszy przedmiot stosuje akcję do pierwszego przedmiotu w grupie. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Otwórz grupę +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Pierwszy przedmiot # Group Management GUI -jei.gui.collapsible.title=Zarządzaj grupami -jei.gui.collapsible.back=Wstecz -jei.gui.collapsible.newGroup=Nowa grupa -jei.gui.collapsible.title.tooltip=Włącz/wyłącz wbudowane grupy i twórz grupy zdefiniowane przez użytkownika. -jei.gui.collapsible.enabled=Włączone -jei.gui.collapsible.disabled=Wyłączone -jei.gui.collapsible.customGroup=Niestandardowe -jei.gui.collapsible.defaultGroup=Domyślne -jei.gui.collapsible.itemCount=%d przedmiotów -jei.gui.collapsible.editor.title=Wybrane przedmioty -jei.gui.collapsible.editor.name=Nazwa -jei.gui.collapsible.editor.save=Zapisz -jei.gui.collapsible.editor.selected=%d wybranych +hei.gui.collapsible.title=Zarządzaj grupami +hei.gui.collapsible.back=Wstecz +hei.gui.collapsible.newGroup=Nowa grupa +hei.gui.collapsible.title.tooltip=Włącz/wyłącz wbudowane grupy i twórz grupy zdefiniowane przez użytkownika. +hei.gui.collapsible.enabled=Włączone +hei.gui.collapsible.disabled=Wyłączone +hei.gui.collapsible.customGroup=Niestandardowe +hei.gui.collapsible.defaultGroup=Domyślne +hei.gui.collapsible.itemCount=%d przedmiotów +hei.gui.collapsible.editor.title=Wybrane przedmioty +hei.gui.collapsible.editor.name=Nazwa +hei.gui.collapsible.editor.save=Zapisz +hei.gui.collapsible.editor.selected=%d wybranych diff --git a/src/main/resources/assets/jei/lang/pt_br.lang b/src/main/resources/assets/jei/lang/pt_br.lang index f4c919a23..43bb1c0db 100644 --- a/src/main/resources/assets/jei/lang/pt_br.lang +++ b/src/main/resources/assets/jei/lang/pt_br.lang @@ -131,33 +131,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Ocultar botão de favorito no config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Se o botão de favorito no canto inferior esquerdo deve ser ocultado # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Clique para expandir -jei.tooltip.collapsed.expand.firstItem=Alt+Clique para o primeiro item -jei.tooltip.collapsed.collapse=Alt+Clique para recolher -jei.tooltip.config.expandCollapseAll=Alt+Clique para alternar grupos - -config.jei.collapsible=Grupos recolhíveis -config.jei.collapsible.comment=Opções para recolher grupos de ingredientes em um único slot na lista de ingredientes. -config.jei.collapsible.collapsibleGroupsEnabled=Habilitar grupos recolhíveis -config.jei.collapsible.collapsibleGroupsEnabled.comment=Quando habilitado, grupos de ingredientes relacionados (poções, livros encantados, etc.) são recolhidos em um único slot. Alt+Clique em um grupo para expandir ou recolher. -config.jei.collapsible.collapseOnClose=Recolher ao fechar -config.jei.collapsible.collapseOnClose.comment=Recolhe os grupos atualmente abertos quando o HEI é fechado. -config.jei.collapsible.collapsedClickAction=Ação de clique padrão -config.jei.collapsible.collapsedClickAction.comment=Troca a ação do clique simples, a outra opção se torna a ação Alt+Clique. Abrir Grupo abre o grupo. Primeiro Item aplica a ação ao primeiro item do grupo. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Abrir Grupo -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Primeiro Item +hei.tooltip.collapsed.expand=Alt+Clique para expandir +hei.tooltip.collapsed.expand.firstItem=Alt+Clique para o primeiro item +hei.tooltip.collapsed.collapse=Alt+Clique para recolher +hei.tooltip.config.expandCollapseAll=Alt+Clique para alternar grupos + +config.hei.collapsible=Grupos recolhíveis +config.hei.collapsible.comment=Opções para recolher grupos de ingredientes em um único slot na lista de ingredientes. +config.hei.collapsible.collapsibleGroupsEnabled=Habilitar grupos recolhíveis +config.hei.collapsible.collapsibleGroupsEnabled.comment=Quando habilitado, grupos de ingredientes relacionados (poções, livros encantados, etc.) são recolhidos em um único slot. Alt+Clique em um grupo para expandir ou recolher. +config.hei.collapsible.collapseOnClose=Recolher ao fechar +config.hei.collapsible.collapseOnClose.comment=Recolhe os grupos atualmente abertos quando o HEI é fechado. +config.hei.collapsible.collapsedClickAction=Ação de clique padrão +config.hei.collapsible.collapsedClickAction.comment=Troca a ação do clique simples, a outra opção se torna a ação Alt+Clique. Abrir Grupo abre o grupo. Primeiro Item aplica a ação ao primeiro item do grupo. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Abrir Grupo +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Primeiro Item # Group Management GUI -jei.gui.collapsible.title=Gerenciar grupos -jei.gui.collapsible.back=Voltar -jei.gui.collapsible.newGroup=Novo grupo -jei.gui.collapsible.title.tooltip=Ativar/desativar grupos integrados e criar grupos definidos pelo usuário. -jei.gui.collapsible.enabled=Ativado -jei.gui.collapsible.disabled=Desativado -jei.gui.collapsible.customGroup=Personalizado -jei.gui.collapsible.defaultGroup=Padrão -jei.gui.collapsible.itemCount=%d itens -jei.gui.collapsible.editor.title=Itens selecionados -jei.gui.collapsible.editor.name=Nome -jei.gui.collapsible.editor.save=Salvar -jei.gui.collapsible.editor.selected=%d selecionados +hei.gui.collapsible.title=Gerenciar grupos +hei.gui.collapsible.back=Voltar +hei.gui.collapsible.newGroup=Novo grupo +hei.gui.collapsible.title.tooltip=Ativar/desativar grupos integrados e criar grupos definidos pelo usuário. +hei.gui.collapsible.enabled=Ativado +hei.gui.collapsible.disabled=Desativado +hei.gui.collapsible.customGroup=Personalizado +hei.gui.collapsible.defaultGroup=Padrão +hei.gui.collapsible.itemCount=%d itens +hei.gui.collapsible.editor.title=Itens selecionados +hei.gui.collapsible.editor.name=Nome +hei.gui.collapsible.editor.save=Salvar +hei.gui.collapsible.editor.selected=%d selecionados diff --git a/src/main/resources/assets/jei/lang/ru_ru.lang b/src/main/resources/assets/jei/lang/ru_ru.lang index a28056a7a..9cc8f83de 100644 --- a/src/main/resources/assets/jei/lang/ru_ru.lang +++ b/src/main/resources/assets/jei/lang/ru_ru.lang @@ -166,33 +166,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Скрыть кнопку з config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Скрывать ли кнопку закладки в левом нижнем углу # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Клик для раскрытия -jei.tooltip.collapsed.expand.firstItem=Alt+Клик для первого предмета -jei.tooltip.collapsed.collapse=Alt+Клик для сворачивания -jei.tooltip.config.expandCollapseAll=Alt+Клик переключить группы - -config.jei.collapsible=Сворачиваемые группы -config.jei.collapsible.comment=Настройки сворачивания групп ингредиентов в один слот в списке ингредиентов. -config.jei.collapsible.collapsibleGroupsEnabled=Включить сворачиваемые группы -config.jei.collapsible.collapsibleGroupsEnabled.comment=При включении группы связанных ингредиентов (зелья, зачарованные книги и т.д.) сворачиваются в один слот. Alt+Клик по группе для раскрытия или сворачивания. -config.jei.collapsible.collapseOnClose=Сворачивать при закрытии -config.jei.collapsible.collapseOnClose.comment=Сворачивать открытые группы при закрытии HEI. -config.jei.collapsible.collapsedClickAction=Действие клика по умолчанию -config.jei.collapsible.collapsedClickAction.comment=Меняет действие одиночного клика, другой вариант становится действием Alt+Клик. Открыть группу открывает группу. Первый предмет применяет действие к первому предмету в группе. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Открыть группу -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Первый предмет +hei.tooltip.collapsed.expand=Alt+Клик для раскрытия +hei.tooltip.collapsed.expand.firstItem=Alt+Клик для первого предмета +hei.tooltip.collapsed.collapse=Alt+Клик для сворачивания +hei.tooltip.config.expandCollapseAll=Alt+Клик переключить группы + +config.hei.collapsible=Сворачиваемые группы +config.hei.collapsible.comment=Настройки сворачивания групп ингредиентов в один слот в списке ингредиентов. +config.hei.collapsible.collapsibleGroupsEnabled=Включить сворачиваемые группы +config.hei.collapsible.collapsibleGroupsEnabled.comment=При включении группы связанных ингредиентов (зелья, зачарованные книги и т.д.) сворачиваются в один слот. Alt+Клик по группе для раскрытия или сворачивания. +config.hei.collapsible.collapseOnClose=Сворачивать при закрытии +config.hei.collapsible.collapseOnClose.comment=Сворачивать открытые группы при закрытии HEI. +config.hei.collapsible.collapsedClickAction=Действие клика по умолчанию +config.hei.collapsible.collapsedClickAction.comment=Меняет действие одиночного клика, другой вариант становится действием Alt+Клик. Открыть группу открывает группу. Первый предмет применяет действие к первому предмету в группе. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Открыть группу +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Первый предмет # Group Management GUI -jei.gui.collapsible.title=Управление группами -jei.gui.collapsible.back=Назад -jei.gui.collapsible.newGroup=Новая группа -jei.gui.collapsible.title.tooltip=Включение/отключение встроенных групп и создание пользовательских групп. -jei.gui.collapsible.enabled=Включено -jei.gui.collapsible.disabled=Отключено -jei.gui.collapsible.customGroup=Пользовательская -jei.gui.collapsible.defaultGroup=По умолчанию -jei.gui.collapsible.itemCount=%d предметов -jei.gui.collapsible.editor.title=Выбранные предметы -jei.gui.collapsible.editor.name=Название -jei.gui.collapsible.editor.save=Сохранить -jei.gui.collapsible.editor.selected=%d выбрано +hei.gui.collapsible.title=Управление группами +hei.gui.collapsible.back=Назад +hei.gui.collapsible.newGroup=Новая группа +hei.gui.collapsible.title.tooltip=Включение/отключение встроенных групп и создание пользовательских групп. +hei.gui.collapsible.enabled=Включено +hei.gui.collapsible.disabled=Отключено +hei.gui.collapsible.customGroup=Пользовательская +hei.gui.collapsible.defaultGroup=По умолчанию +hei.gui.collapsible.itemCount=%d предметов +hei.gui.collapsible.editor.title=Выбранные предметы +hei.gui.collapsible.editor.name=Название +hei.gui.collapsible.editor.save=Сохранить +hei.gui.collapsible.editor.selected=%d выбрано diff --git a/src/main/resources/assets/jei/lang/sv_se.lang b/src/main/resources/assets/jei/lang/sv_se.lang index 74b6928e5..f199ffe12 100644 --- a/src/main/resources/assets/jei/lang/sv_se.lang +++ b/src/main/resources/assets/jei/lang/sv_se.lang @@ -134,33 +134,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Dölj bokmärkesknapp i nedre config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Om bokmärkesknappen i nedre vänstra hörnet ska döljas # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Klicka för att expandera -jei.tooltip.collapsed.expand.firstItem=Alt+Klicka för första föremålet -jei.tooltip.collapsed.collapse=Alt+Klicka för att fälla ihop -jei.tooltip.config.expandCollapseAll=Alt+Klicka för att växla grupper - -config.jei.collapsible=Ihopfällbara grupper -config.jei.collapsible.comment=Alternativ för att fälla ihop grupper av ingredienser till en enda plats i ingredienslistan. -config.jei.collapsible.collapsibleGroupsEnabled=Aktivera ihopfällbara grupper -config.jei.collapsible.collapsibleGroupsEnabled.comment=När aktiverat fälls grupper av relaterade ingredienser (bryggdrycker, förtrollade böcker osv.) ihop till en enda plats. Alt+Klicka på en grupp för att expandera eller fälla ihop den. -config.jei.collapsible.collapseOnClose=Fäll ihop vid stängning -config.jei.collapsible.collapseOnClose.comment=Fäller ihop öppna grupper när HEI stängs. -config.jei.collapsible.collapsedClickAction=Standardklickåtgärd -config.jei.collapsible.collapsedClickAction.comment=Byter enkelklicksåtgärden, det andra alternativet blir Alt+Klick-åtgärden. Öppna grupp öppnar gruppen. Första föremålet tillämpar åtgärden på gruppens första föremål. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Öppna grupp -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Första föremålet +hei.tooltip.collapsed.expand=Alt+Klicka för att expandera +hei.tooltip.collapsed.expand.firstItem=Alt+Klicka för första föremålet +hei.tooltip.collapsed.collapse=Alt+Klicka för att fälla ihop +hei.tooltip.config.expandCollapseAll=Alt+Klicka för att växla grupper + +config.hei.collapsible=Ihopfällbara grupper +config.hei.collapsible.comment=Alternativ för att fälla ihop grupper av ingredienser till en enda plats i ingredienslistan. +config.hei.collapsible.collapsibleGroupsEnabled=Aktivera ihopfällbara grupper +config.hei.collapsible.collapsibleGroupsEnabled.comment=När aktiverat fälls grupper av relaterade ingredienser (bryggdrycker, förtrollade böcker osv.) ihop till en enda plats. Alt+Klicka på en grupp för att expandera eller fälla ihop den. +config.hei.collapsible.collapseOnClose=Fäll ihop vid stängning +config.hei.collapsible.collapseOnClose.comment=Fäller ihop öppna grupper när HEI stängs. +config.hei.collapsible.collapsedClickAction=Standardklickåtgärd +config.hei.collapsible.collapsedClickAction.comment=Byter enkelklicksåtgärden, det andra alternativet blir Alt+Klick-åtgärden. Öppna grupp öppnar gruppen. Första föremålet tillämpar åtgärden på gruppens första föremål. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Öppna grupp +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Första föremålet # Group Management GUI -jei.gui.collapsible.title=Hantera grupper -jei.gui.collapsible.back=Tillbaka -jei.gui.collapsible.newGroup=Ny grupp -jei.gui.collapsible.title.tooltip=Aktivera/inaktivera inbyggda grupper och skapa användardefinierade grupper. -jei.gui.collapsible.enabled=Aktiverad -jei.gui.collapsible.disabled=Inaktiverad -jei.gui.collapsible.customGroup=Anpassad -jei.gui.collapsible.defaultGroup=Standard -jei.gui.collapsible.itemCount=%d föremål -jei.gui.collapsible.editor.title=Valda föremål -jei.gui.collapsible.editor.name=Namn -jei.gui.collapsible.editor.save=Spara -jei.gui.collapsible.editor.selected=%d valda +hei.gui.collapsible.title=Hantera grupper +hei.gui.collapsible.back=Tillbaka +hei.gui.collapsible.newGroup=Ny grupp +hei.gui.collapsible.title.tooltip=Aktivera/inaktivera inbyggda grupper och skapa användardefinierade grupper. +hei.gui.collapsible.enabled=Aktiverad +hei.gui.collapsible.disabled=Inaktiverad +hei.gui.collapsible.customGroup=Anpassad +hei.gui.collapsible.defaultGroup=Standard +hei.gui.collapsible.itemCount=%d föremål +hei.gui.collapsible.editor.title=Valda föremål +hei.gui.collapsible.editor.name=Namn +hei.gui.collapsible.editor.save=Spara +hei.gui.collapsible.editor.selected=%d valda diff --git a/src/main/resources/assets/jei/lang/tr_tr.lang b/src/main/resources/assets/jei/lang/tr_tr.lang index 1d8cc49f5..8c9dc3d7d 100644 --- a/src/main/resources/assets/jei/lang/tr_tr.lang +++ b/src/main/resources/assets/jei/lang/tr_tr.lang @@ -121,33 +121,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Sol alt köşedeki yer imi d config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Sol alt köşedeki yer imi düğmesinin gizlenip gizlenmeyeceği # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Tıkla genişletmek için -jei.tooltip.collapsed.expand.firstItem=Alt+Tıkla ilk öğe için -jei.tooltip.collapsed.collapse=Alt+Tıkla daraltmak için -jei.tooltip.config.expandCollapseAll=Alt+Tıkla Grupları Dönüştür - -config.jei.collapsible=Daraltılabilir Gruplar -config.jei.collapsible.comment=Malzeme listesinde malzeme gruplarını tek bir yuvaya daraltma seçenekleri. -config.jei.collapsible.collapsibleGroupsEnabled=Daraltılabilir Grupları Etkinleştir -config.jei.collapsible.collapsibleGroupsEnabled.comment=Etkinleştirildiğinde, ilgili malzeme grupları (iksirler, büyülü kitaplar vb.) tek bir yuvaya daraltılır. Alt+Tıkla ile bir grubu genişletin veya daraltın. -config.jei.collapsible.collapseOnClose=Kapatırken Daralt -config.jei.collapsible.collapseOnClose.comment=HEI kapatıldığında açık olan grupları daraltır. -config.jei.collapsible.collapsedClickAction=Varsayılan Tıklama Eylemi -config.jei.collapsible.collapsedClickAction.comment=Tek tıklama eylemini değiştirir, diğer seçenek Alt+Tıklama eylemi olur. Grubu Aç grubu açar. İlk Öğe eylemi gruptaki ilk öğeye uygular. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Grubu Aç -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=İlk Öğe +hei.tooltip.collapsed.expand=Alt+Tıkla genişletmek için +hei.tooltip.collapsed.expand.firstItem=Alt+Tıkla ilk öğe için +hei.tooltip.collapsed.collapse=Alt+Tıkla daraltmak için +hei.tooltip.config.expandCollapseAll=Alt+Tıkla Grupları Dönüştür + +config.hei.collapsible=Daraltılabilir Gruplar +config.hei.collapsible.comment=Malzeme listesinde malzeme gruplarını tek bir yuvaya daraltma seçenekleri. +config.hei.collapsible.collapsibleGroupsEnabled=Daraltılabilir Grupları Etkinleştir +config.hei.collapsible.collapsibleGroupsEnabled.comment=Etkinleştirildiğinde, ilgili malzeme grupları (iksirler, büyülü kitaplar vb.) tek bir yuvaya daraltılır. Alt+Tıkla ile bir grubu genişletin veya daraltın. +config.hei.collapsible.collapseOnClose=Kapatırken Daralt +config.hei.collapsible.collapseOnClose.comment=HEI kapatıldığında açık olan grupları daraltır. +config.hei.collapsible.collapsedClickAction=Varsayılan Tıklama Eylemi +config.hei.collapsible.collapsedClickAction.comment=Tek tıklama eylemini değiştirir, diğer seçenek Alt+Tıklama eylemi olur. Grubu Aç grubu açar. İlk Öğe eylemi gruptaki ilk öğeye uygular. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Grubu Aç +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=İlk Öğe # Group Management GUI -jei.gui.collapsible.title=Grupları Yönet -jei.gui.collapsible.back=Geri -jei.gui.collapsible.newGroup=Yeni Grup -jei.gui.collapsible.title.tooltip=Yerleşik grupları etkinleştirin/devre dışı bırakın ve kullanıcı tanımlı gruplar oluşturun. -jei.gui.collapsible.enabled=Etkin -jei.gui.collapsible.disabled=Devre dışı -jei.gui.collapsible.customGroup=Özel -jei.gui.collapsible.defaultGroup=Varsayılan -jei.gui.collapsible.itemCount=%d öğe -jei.gui.collapsible.editor.title=Seçili Öğeler -jei.gui.collapsible.editor.name=Ad -jei.gui.collapsible.editor.save=Kaydet -jei.gui.collapsible.editor.selected=%d seçildi +hei.gui.collapsible.title=Grupları Yönet +hei.gui.collapsible.back=Geri +hei.gui.collapsible.newGroup=Yeni Grup +hei.gui.collapsible.title.tooltip=Yerleşik grupları etkinleştirin/devre dışı bırakın ve kullanıcı tanımlı gruplar oluşturun. +hei.gui.collapsible.enabled=Etkin +hei.gui.collapsible.disabled=Devre dışı +hei.gui.collapsible.customGroup=Özel +hei.gui.collapsible.defaultGroup=Varsayılan +hei.gui.collapsible.itemCount=%d öğe +hei.gui.collapsible.editor.title=Seçili Öğeler +hei.gui.collapsible.editor.name=Ad +hei.gui.collapsible.editor.save=Kaydet +hei.gui.collapsible.editor.selected=%d seçildi diff --git a/src/main/resources/assets/jei/lang/uk_ua.lang b/src/main/resources/assets/jei/lang/uk_ua.lang index a69cc8091..b7739815c 100644 --- a/src/main/resources/assets/jei/lang/uk_ua.lang +++ b/src/main/resources/assets/jei/lang/uk_ua.lang @@ -123,33 +123,33 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=Приховати кнопк config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=Чи приховувати кнопку закладки в лівому нижньому куті # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+Клік для розгортання -jei.tooltip.collapsed.expand.firstItem=Alt+Клік для першого предмета -jei.tooltip.collapsed.collapse=Alt+Клік для згортання -jei.tooltip.config.expandCollapseAll=Alt+Клік перемкнути групи - -config.jei.collapsible=Згортувані групи -config.jei.collapsible.comment=Налаштування згортання груп інгредієнтів в один слот у списку інгредієнтів. -config.jei.collapsible.collapsibleGroupsEnabled=Увімкнути згортувані групи -config.jei.collapsible.collapsibleGroupsEnabled.comment=Коли увімкнено, групи пов'язаних інгредієнтів (зілля, зачаровані книги тощо) згортаються в один слот. Alt+Клік по групі для розгортання або згортання. -config.jei.collapsible.collapseOnClose=Згорнути при закритті -config.jei.collapsible.collapseOnClose.comment=Згортає відкриті групи при закритті HEI. -config.jei.collapsible.collapsedClickAction=Дія кліку за замовчуванням -config.jei.collapsible.collapsedClickAction.comment=Змінює дію одиничного кліку, інший варіант стає дією Alt+Клік. Відкрити групу відкриває групу. Перший предмет застосовує дію до першого предмета в групі. -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=Відкрити групу -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=Перший предмет +hei.tooltip.collapsed.expand=Alt+Клік для розгортання +hei.tooltip.collapsed.expand.firstItem=Alt+Клік для першого предмета +hei.tooltip.collapsed.collapse=Alt+Клік для згортання +hei.tooltip.config.expandCollapseAll=Alt+Клік перемкнути групи + +config.hei.collapsible=Згортувані групи +config.hei.collapsible.comment=Налаштування згортання груп інгредієнтів в один слот у списку інгредієнтів. +config.hei.collapsible.collapsibleGroupsEnabled=Увімкнути згортувані групи +config.hei.collapsible.collapsibleGroupsEnabled.comment=Коли увімкнено, групи пов'язаних інгредієнтів (зілля, зачаровані книги тощо) згортаються в один слот. Alt+Клік по групі для розгортання або згортання. +config.hei.collapsible.collapseOnClose=Згорнути при закритті +config.hei.collapsible.collapseOnClose.comment=Згортає відкриті групи при закритті HEI. +config.hei.collapsible.collapsedClickAction=Дія кліку за замовчуванням +config.hei.collapsible.collapsedClickAction.comment=Змінює дію одиничного кліку, інший варіант стає дією Alt+Клік. Відкрити групу відкриває групу. Перший предмет застосовує дію до першого предмета в групі. +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=Відкрити групу +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=Перший предмет # Group Management GUI -jei.gui.collapsible.title=Управління групами -jei.gui.collapsible.back=Назад -jei.gui.collapsible.newGroup=Нова група -jei.gui.collapsible.title.tooltip=Увімкнення/вимкнення вбудованих груп та створення користувацьких груп. -jei.gui.collapsible.enabled=Увімкнено -jei.gui.collapsible.disabled=Вимкнено -jei.gui.collapsible.customGroup=Користувацька -jei.gui.collapsible.defaultGroup=За замовчуванням -jei.gui.collapsible.itemCount=%d предметів -jei.gui.collapsible.editor.title=Вибрані предмети -jei.gui.collapsible.editor.name=Назва -jei.gui.collapsible.editor.save=Зберегти -jei.gui.collapsible.editor.selected=%d вибрано +hei.gui.collapsible.title=Управління групами +hei.gui.collapsible.back=Назад +hei.gui.collapsible.newGroup=Нова група +hei.gui.collapsible.title.tooltip=Увімкнення/вимкнення вбудованих груп та створення користувацьких груп. +hei.gui.collapsible.enabled=Увімкнено +hei.gui.collapsible.disabled=Вимкнено +hei.gui.collapsible.customGroup=Користувацька +hei.gui.collapsible.defaultGroup=За замовчуванням +hei.gui.collapsible.itemCount=%d предметів +hei.gui.collapsible.editor.title=Вибрані предмети +hei.gui.collapsible.editor.name=Назва +hei.gui.collapsible.editor.save=Зберегти +hei.gui.collapsible.editor.selected=%d вибрано diff --git a/src/main/resources/assets/jei/lang/zh_cn.lang b/src/main/resources/assets/jei/lang/zh_cn.lang index 2cc670935..ec026b902 100644 --- a/src/main/resources/assets/jei/lang/zh_cn.lang +++ b/src/main/resources/assets/jei/lang/zh_cn.lang @@ -163,33 +163,35 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=隐藏左下角书签按钮 config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隐藏左下角书签按钮 # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+点击展开 -jei.tooltip.collapsed.expand.firstItem=Alt+点击查看第一个物品 -jei.tooltip.collapsed.collapse=Alt+点击收起 -jei.tooltip.config.expandCollapseAll=Alt+点击切换分组 - -config.jei.collapsible=可折叠分组 -config.jei.collapsible.comment=将配料分组折叠为配料列表中的单个格子的相关选项。 -config.jei.collapsible.collapsibleGroupsEnabled=启用可折叠分组 -config.jei.collapsible.collapsibleGroupsEnabled.comment=启用后,相关配料的分组(药水、附魔书等)将折叠为单个格子。Alt+点击分组以展开或折叠。 -config.jei.collapsible.collapseOnClose=关闭时折叠 -config.jei.collapsible.collapseOnClose.comment=关闭HEI时折叠当前已展开的分组。 -config.jei.collapsible.collapsedClickAction=默认点击操作 -config.jei.collapsible.collapsedClickAction.comment=切换单击操作,另一个选项变为Alt+点击操作。打开分组会展开分组。第一个物品会对分组中的第一个物品执行操作。 -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=打开分组 -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=第一个物品 +hei.tooltip.collapsed.expand=Alt+点击展开 +hei.tooltip.collapsed.expand.firstItem=Alt+点击查看第一个物品 +hei.tooltip.collapsed.collapse=Alt+点击收起 +hei.tooltip.config.expandCollapseAll=Alt+点击切换分组 + +config.hei.collapsible=可折叠分组 +config.hei.collapsible.comment=将配料分组折叠为配料列表中的单个格子的相关选项。 +config.hei.collapsible.collapsibleGroupsEnabled=启用可折叠分组 +config.hei.collapsible.collapsibleGroupsEnabled.comment=启用后,相关配料的分组(药水、附魔书等)将折叠为单个格子。Alt+点击分组以展开或折叠。 +config.hei.collapsible.collapseOnClose=关闭时折叠 +config.hei.collapsible.collapseOnClose.comment=关闭HEI时折叠当前已展开的分组。 +config.hei.collapsible.collapsedClickAction=默认点击操作 +config.hei.collapsible.collapsedClickAction.comment=切换单击操作,另一个选项变为Alt+点击操作。打开分组会展开分组。第一个物品会对分组中的第一个物品执行操作。 +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=打开分组 +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=第一个物品 # Group Management GUI -jei.gui.collapsible.title=管理分组 -jei.gui.collapsible.back=返回 -jei.gui.collapsible.newGroup=新建分组 -jei.gui.collapsible.title.tooltip=启用/禁用内置分组并创建用户自定义分组。 -jei.gui.collapsible.enabled=已启用 -jei.gui.collapsible.disabled=已禁用 -jei.gui.collapsible.customGroup=自定义 -jei.gui.collapsible.defaultGroup=默认 -jei.gui.collapsible.itemCount=%d 个物品 -jei.gui.collapsible.editor.title=已选物品 -jei.gui.collapsible.editor.name=名称 -jei.gui.collapsible.editor.save=保存 -jei.gui.collapsible.editor.selected=%d 已选择 +hei.gui.collapsible.title=管理分组 +hei.gui.collapsible.back=返回 +hei.gui.collapsible.newGroup=新建分组 +hei.gui.collapsible.title.tooltip=启用/禁用内置分组并创建用户自定义分组。 +hei.gui.collapsible.enabled=已启用 +hei.gui.collapsible.disabled=已禁用 +hei.gui.collapsible.customGroup=自定义 +hei.gui.collapsible.modGroup=Mod +hei.gui.collapsible.defaultGroup=默认 +hei.gui.collapsible.itemCount=%d 个物品 +hei.gui.collapsible.editor.title=已选物品 +hei.gui.collapsible.editor.name=名称 +hei.gui.collapsible.editor.save=保存 +hei.gui.collapsible.editor.selected=%d 已选择 +hei.gui.collapsible.confirmDelete=删除? diff --git a/src/main/resources/assets/jei/lang/zh_tw.lang b/src/main/resources/assets/jei/lang/zh_tw.lang index 015bed41b..2967625cd 100644 --- a/src/main/resources/assets/jei/lang/zh_tw.lang +++ b/src/main/resources/assets/jei/lang/zh_tw.lang @@ -131,33 +131,35 @@ config.jei.misc.hideBottomLeftCornerBookmarkButton=隱藏左下角書籤按鈕 config.jei.misc.hideBottomLeftCornerBookmarkButton.comment=是否隱藏左下角書籤按鈕 # Collapsible Groups -jei.tooltip.collapsed.expand=Alt+點擊展開 -jei.tooltip.collapsed.expand.firstItem=Alt+點擊查看第一個物品 -jei.tooltip.collapsed.collapse=Alt+點擊收合 -jei.tooltip.config.expandCollapseAll=Alt+點擊切換群組 - -config.jei.collapsible=可折疊群組 -config.jei.collapsible.comment=將材料群組折疊為材料列表中的單個格子的相關選項。 -config.jei.collapsible.collapsibleGroupsEnabled=啟用可折疊群組 -config.jei.collapsible.collapsibleGroupsEnabled.comment=啟用後,相關材料的群組(藥水、附魔書等)將折疊為單個格子。Alt+點擊群組以展開或收合。 -config.jei.collapsible.collapseOnClose=關閉時收合 -config.jei.collapsible.collapseOnClose.comment=關閉HEI時收合目前已展開的群組。 -config.jei.collapsible.collapsedClickAction=預設點擊操作 -config.jei.collapsible.collapsedClickAction.comment=切換單擊操作,另一個選項變為Alt+點擊操作。開啟群組會展開群組。第一個物品會對群組中的第一個物品執行操作。 -config.jei.collapsible.collapsedClickAction.OPEN_GROUP=開啟群組 -config.jei.collapsible.collapsedClickAction.FIRST_ITEM=第一個物品 +hei.tooltip.collapsed.expand=Alt+點擊展開 +hei.tooltip.collapsed.expand.firstItem=Alt+點擊查看第一個物品 +hei.tooltip.collapsed.collapse=Alt+點擊收合 +hei.tooltip.config.expandCollapseAll=Alt+點擊切換群組 + +config.hei.collapsible=可折疊群組 +config.hei.collapsible.comment=將材料群組折疊為材料列表中的單個格子的相關選項。 +config.hei.collapsible.collapsibleGroupsEnabled=啟用可折疊群組 +config.hei.collapsible.collapsibleGroupsEnabled.comment=啟用後,相關材料的群組(藥水、附魔書等)將折疊為單個格子。Alt+點擊群組以展開或收合。 +config.hei.collapsible.collapseOnClose=關閉時收合 +config.hei.collapsible.collapseOnClose.comment=關閉HEI時收合目前已展開的群組。 +config.hei.collapsible.collapsedClickAction=預設點擊操作 +config.hei.collapsible.collapsedClickAction.comment=切換單擊操作,另一個選項變為Alt+點擊操作。開啟群組會展開群組。第一個物品會對群組中的第一個物品執行操作。 +config.hei.collapsible.collapsedClickAction.OPEN_GROUP=開啟群組 +config.hei.collapsible.collapsedClickAction.FIRST_ITEM=第一個物品 # Group Management GUI -jei.gui.collapsible.title=管理群組 -jei.gui.collapsible.back=返回 -jei.gui.collapsible.newGroup=新建群組 -jei.gui.collapsible.title.tooltip=啟用/停用內建群組並建立使用者自訂群組。 -jei.gui.collapsible.enabled=已啟用 -jei.gui.collapsible.disabled=已停用 -jei.gui.collapsible.customGroup=自訂 -jei.gui.collapsible.defaultGroup=預設 -jei.gui.collapsible.itemCount=%d 個物品 -jei.gui.collapsible.editor.title=已選物品 -jei.gui.collapsible.editor.name=名稱 -jei.gui.collapsible.editor.save=儲存 -jei.gui.collapsible.editor.selected=%d 已選擇 +hei.gui.collapsible.title=管理群組 +hei.gui.collapsible.back=返回 +hei.gui.collapsible.newGroup=新建群組 +hei.gui.collapsible.title.tooltip=啟用/停用內建群組並建立使用者自訂群組。 +hei.gui.collapsible.enabled=已啟用 +hei.gui.collapsible.disabled=已停用 +hei.gui.collapsible.customGroup=自訂 +hei.gui.collapsible.modGroup=Mod +hei.gui.collapsible.defaultGroup=預設 +hei.gui.collapsible.itemCount=%d 個物品 +hei.gui.collapsible.editor.title=已選物品 +hei.gui.collapsible.editor.name=名稱 +hei.gui.collapsible.editor.save=儲存 +hei.gui.collapsible.editor.selected=%d 已選擇 +hei.gui.collapsible.confirmDelete=刪除? From b1167af7945ab93a5b3c3f217e975213b04ee3b9 Mon Sep 17 00:00:00 2001 From: Tommy Date: Fri, 3 Apr 2026 21:10:58 -0400 Subject: [PATCH 48/48] Cleanup --- .gitignore | 4 +--- TODO.md | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 TODO.md diff --git a/.gitignore b/.gitignore index b9304af82..1d6848fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,4 @@ changelog.html \.classpath \.project \.settings/ -*.launch -/Reference -/.vscode +*.launch \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 464090415..000000000 --- a/TODO.md +++ /dev/null @@ -1 +0,0 @@ -# TODO