From 79373573c6e0c891d2722022f7fec2c9635baca8 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 12 Feb 2020 15:02:10 +0800 Subject: FilteringEntry Signed-off-by: shedaniel --- .../me/shedaniel/rei/gui/OverlaySearchField.java | 18 +- .../rei/gui/config/entry/FilteringEntry.java | 534 +++++++++++++++++++++ .../rei/gui/config/entry/NoFilteringEntry.java | 65 +++ .../shedaniel/rei/gui/widget/EntryListWidget.java | 126 +---- .../rei/gui/widget/FavoritesListWidget.java | 2 +- .../shedaniel/rei/gui/widget/TextFieldWidget.java | 12 - .../me/shedaniel/rei/impl/ConfigManagerImpl.java | 9 + .../me/shedaniel/rei/impl/EntryRegistryImpl.java | 4 +- .../java/me/shedaniel/rei/impl/SearchArgument.java | 116 +++++ 9 files changed, 743 insertions(+), 143 deletions(-) create mode 100644 src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java create mode 100644 src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java (limited to 'src/main/java') diff --git a/src/main/java/me/shedaniel/rei/gui/OverlaySearchField.java b/src/main/java/me/shedaniel/rei/gui/OverlaySearchField.java index 5fbac5502..a29ba5273 100644 --- a/src/main/java/me/shedaniel/rei/gui/OverlaySearchField.java +++ b/src/main/java/me/shedaniel/rei/gui/OverlaySearchField.java @@ -29,15 +29,16 @@ public class OverlaySearchField extends TextFieldWidget { public int keybindFocusKey = -1; protected Pair lastClickedDetails = null; private List history = Lists.newArrayListWithCapacity(100); + public boolean isMain = true; - OverlaySearchField(int x, int y, int width, int height) { + public OverlaySearchField(int x, int y, int width, int height) { super(x, y, width, height); setMaxLength(10000); } @Override public void setFocused(boolean boolean_1) { - if (isFocused() != boolean_1) + if (isFocused() != boolean_1 && isMain) addToHistory(getText()); super.setFocused(boolean_1); } @@ -54,7 +55,7 @@ public class OverlaySearchField extends TextFieldWidget { public void laterRender(int int_1, int int_2, float float_1) { RenderSystem.disableDepthTest(); - setEditableColor(ContainerScreenOverlay.getEntryListWidget().getAllStacks().isEmpty() && !getText().isEmpty() ? 16733525 : isSearching ? -852212 : (containsMouse(PointHelper.fromMouse()) || isFocused()) ? (ScreenHelper.isDarkModeEnabled() ? -17587 : -1) : -6250336); + setEditableColor(isMain && ContainerScreenOverlay.getEntryListWidget().getAllStacks().isEmpty() && !getText().isEmpty() ? 16733525 : isSearching && isMain ? -852212 : (containsMouse(PointHelper.fromMouse()) || isFocused()) ? (ScreenHelper.isDarkModeEnabled() ? -17587 : -1) : -6250336); setSuggestion(!isFocused() && getText().isEmpty() ? I18n.translate("text.rei.search.field.suggestion") : null); super.render(int_1, int_2, float_1); RenderSystem.enableDepthTest(); @@ -70,10 +71,9 @@ public class OverlaySearchField extends TextFieldWidget { @Override public void renderBorder() { - boolean hasError = ContainerScreenOverlay.getEntryListWidget().getAllStacks().isEmpty() && !getText().isEmpty(); - if (isSearching) { + if (isMain && isSearching) { fill(this.getBounds().x - 1, this.getBounds().y - 1, this.getBounds().x + this.getBounds().width + 1, this.getBounds().y + this.getBounds().height + 1, -852212); - } else if (hasError) { + } else if (isMain && ContainerScreenOverlay.getEntryListWidget().getAllStacks().isEmpty() && !getText().isEmpty()) { fill(this.getBounds().x - 1, this.getBounds().y - 1, this.getBounds().x + this.getBounds().width + 1, this.getBounds().y + this.getBounds().height + 1, -43691); } else { super.renderBorder(); @@ -93,7 +93,7 @@ public class OverlaySearchField extends TextFieldWidget { boolean contains = containsMouse(double_1, double_2); if (isVisible() && contains && int_1 == 1) setText(""); - if (contains && int_1 == 0) + if (contains && int_1 == 0 && isMain) if (lastClickedDetails == null) lastClickedDetails = new Pair<>(System.currentTimeMillis(), new Point(double_1, double_2)); else if (System.currentTimeMillis() - lastClickedDetails.getLeft() > 1500) @@ -110,7 +110,7 @@ public class OverlaySearchField extends TextFieldWidget { @Override public boolean keyPressed(int int_1, int int_2, int int_3) { - if (this.isVisible() && this.isFocused()) + if (this.isVisible() && this.isFocused() && isMain) if (int_1 == 257 || int_1 == 335) { addToHistory(getText()); setFocused(false); @@ -149,7 +149,7 @@ public class OverlaySearchField extends TextFieldWidget { @Override public boolean containsMouse(double mouseX, double mouseY) { - return ScreenHelper.getLastOverlay().isNotInExclusionZones(mouseX, mouseY) && super.containsMouse(mouseX, mouseY); + return (!isMain || ScreenHelper.getLastOverlay().isNotInExclusionZones(mouseX, mouseY)) && super.containsMouse(mouseX, mouseY); } @Override diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java new file mode 100644 index 000000000..b0962789d --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2018, 2019, 2020 shedaniel + * Licensed under the MIT License (the "License"). + */ + +package me.shedaniel.rei.gui.config.entry; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.systems.RenderSystem; +import me.shedaniel.clothconfig2.ClothConfigInitializer; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import me.shedaniel.clothconfig2.gui.ClothConfigScreen; +import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget; +import me.shedaniel.math.api.Point; +import me.shedaniel.math.api.Rectangle; +import me.shedaniel.math.impl.PointHelper; +import me.shedaniel.rei.api.ConfigObject; +import me.shedaniel.rei.api.EntryRegistry; +import me.shedaniel.rei.api.EntryStack; +import me.shedaniel.rei.gui.OverlaySearchField; +import me.shedaniel.rei.gui.widget.EntryWidget; +import me.shedaniel.rei.gui.widget.QueuedTooltip; +import me.shedaniel.rei.impl.ScreenHelper; +import me.shedaniel.rei.impl.SearchArgument; +import me.shedaniel.rei.utils.CollectionUtils; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.util.math.MathHelper; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +import static me.shedaniel.rei.gui.widget.EntryListWidget.entrySize; + +@ApiStatus.Internal +public class FilteringEntry extends AbstractConfigListEntry> { + private Consumer> saveConsumer; + private List defaultValue; + private List configFiltered; + + private QueuedTooltip tooltip = null; + + @SuppressWarnings("rawtypes") private ClothConfigScreen.ListWidget lastList = null; + + protected List selected = Lists.newArrayList(); + + protected double target; + protected double scroll; + protected long start; + protected long duration; + private List entryStacks = null; + private Rectangle innerBounds; + private List entries = Collections.emptyList(); + private List elements = Collections.emptyList(); + private boolean draggingScrollBar = false; + + private Point selectionPoint = null; + private Point secondPoint = null; + + private OverlaySearchField searchField; + private ButtonWidget selectAllButton; + private ButtonWidget selectNoneButton; + private ButtonWidget hideButton; + private ButtonWidget showButton; + + private List lastSearchArguments = Collections.emptyList(); + + public FilteringEntry(List configFiltered, List defaultValue, Consumer> saveConsumer) { + super("", false); + this.configFiltered = configFiltered; + this.defaultValue = defaultValue; + this.saveConsumer = saveConsumer; + this.searchField = new OverlaySearchField(0, 0, 0, 0); + { + String selectAllText = I18n.translate("config.roughlyenoughitems.filteredEntries.selectAll"); + this.selectAllButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getStringWidth(selectAllText) + 10, 20, selectAllText, button -> { + this.selectionPoint = new Point(-Integer.MAX_VALUE / 2, -Integer.MAX_VALUE / 2); + this.secondPoint = new Point(Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2); + }); + } + { + String selectNoneText = I18n.translate("config.roughlyenoughitems.filteredEntries.selectNone"); + this.selectNoneButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getStringWidth(selectNoneText) + 10, 20, selectNoneText, button -> { + this.selectionPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + this.secondPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + }); + } + { + String hideText = I18n.translate("config.roughlyenoughitems.filteredEntries.hide"); + this.hideButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getStringWidth(hideText) + 10, 20, hideText, button -> { + for (int i = 0; i < entryStacks.size(); i++) { + EntryStack stack = entryStacks.get(i); + EntryListEntry entry = entries.get(i); + entry.getBounds().y = (int) (entry.backupY - scroll); + if (entry.isSelected() && !entry.isFiltered()) { + configFiltered.add(stack); + getScreen().setEdited(true, false); + } + } + }); + } + { + String showText = I18n.translate("config.roughlyenoughitems.filteredEntries.show"); + this.showButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getStringWidth(showText) + 10, 20, showText, button -> { + for (int i = 0; i < entryStacks.size(); i++) { + EntryStack stack = entryStacks.get(i); + EntryListEntry entry = entries.get(i); + entry.getBounds().y = (int) (entry.backupY - scroll); + if (entry.isSelected() && configFiltered.remove(stack)) { + getScreen().setEdited(true, false); + } + } + }); + } + this.searchField.isMain = false; + } + + @SuppressWarnings("rawtypes") + public Rectangle getBounds() { + ClothConfigScreen.ListWidget listWidget = getParent(); + return new Rectangle(listWidget.left, listWidget.top, listWidget.right - listWidget.left, listWidget.bottom - listWidget.top); + } + + @Override + public List getValue() { + return configFiltered; + } + + @Override + public Optional> getDefaultValue() { + return Optional.ofNullable(defaultValue); + } + + @Override + public void save() { + saveConsumer.accept(getValue()); + } + + @SuppressWarnings("rawtypes") + @Override + public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + ClothConfigScreen.ListWidget parent = getParent(); + Rectangle bounds = getBounds(); + if (lastList != parent) { + updateSearch(this.searchField.getText()); + lastList = parent; + this.searchField.getBounds().setBounds(bounds.getCenterX() - 75, bounds.getMaxY() - 22, 150, 18); + this.selectAllButton.x = 2; + this.selectAllButton.y = bounds.getMaxY() - 22; + this.selectNoneButton.x = 4 + selectAllButton.getWidth(); + this.selectNoneButton.y = bounds.getMaxY() - 22; + this.hideButton.x = bounds.getMaxX() - hideButton.getWidth() - showButton.getWidth() - 4; + this.hideButton.y = bounds.getMaxY() - 22; + this.showButton.x = bounds.getMaxX() - showButton.getWidth() - 2; + this.showButton.y = bounds.getMaxY() - 22; + this.searchField.setChangedListener(this::updateSearch); + } + tooltip = null; + if (bounds.isEmpty()) + return; + for (EntryListEntry entry : entries) + entry.clearStacks(); + int skip = Math.max(0, MathHelper.floor(scroll / (float) entrySize())); + int nextIndex = skip * innerBounds.width / entrySize(); + int i = nextIndex; + for (; i < entryStacks.size(); i++) { + EntryStack stack = entryStacks.get(i); + EntryListEntry entry = entries.get(nextIndex); + entry.getBounds().y = (int) (entry.backupY - scroll); + if (entry.getBounds().y > bounds.getMaxY()) + break; + entry.entry(stack); + entry.render(mouseX, mouseY, delta); + nextIndex++; + } + updatePosition(delta); + renderScrollbar(); + RenderSystem.translatef(0, 0, 300); + this.searchField.laterRender(mouseX, mouseY, delta); + this.selectAllButton.render(mouseX, mouseY, delta); + this.selectNoneButton.render(mouseX, mouseY, delta); + this.hideButton.render(mouseX, mouseY, delta); + this.showButton.render(mouseX, mouseY, delta); + RenderSystem.translatef(0, 0, -300); + if (tooltip != null) { + ScreenHelper.getLastOverlay().renderTooltip(tooltip); + } + } + + private Rectangle getSelection() { + if (selectionPoint != null) { + Point p = secondPoint; + if (p == null) { + p = PointHelper.fromMouse(); + p.translate(0, (int) scroll); + } + int left = Math.min(p.x, selectionPoint.x); + int top = Math.min(p.y, selectionPoint.y); + int right = Math.max(p.x, selectionPoint.x); + int bottom = Math.max(p.y, selectionPoint.y); + return new Rectangle(left, (int) (top - scroll), right - left, bottom - top); + } + return new Rectangle(0, 0, 0, 0); + } + + private int getScrollbarMinX() { + return getParent().right - 7; + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int int_1, double double_3, double double_4) { + if (int_1 == 0 && draggingScrollBar) { + float height = getMaxScrollPosition(); + int actualHeight = innerBounds.height; + if (height > actualHeight && mouseY >= innerBounds.y && mouseY <= innerBounds.getMaxY()) { + double double_5 = Math.max(1, this.getMaxScroll()); + int int_2 = innerBounds.height; + int int_3 = MathHelper.clamp((int) ((float) (int_2 * int_2) / (float) getMaxScrollPosition()), 32, int_2 - 8); + double double_6 = Math.max(1.0D, double_5 / (double) (int_2 - int_3)); + float to = MathHelper.clamp((float) (scroll + double_4 * double_6), 0, height - innerBounds.height); + if (ConfigObject.getInstance().doesSnapToRows()) { + double nearestRow = Math.round(to / (double) entrySize()) * (double) entrySize(); + scrollTo(nearestRow, false); + } else + scrollTo(to, false); + } + return true; + } + return super.mouseDragged(mouseX, mouseY, int_1, double_3, double_4); + } + + private void renderScrollbar() { + int maxScroll = getMaxScroll(); + if (maxScroll > 0) { + int height = innerBounds.height * innerBounds.height / getMaxScrollPosition(); + height = MathHelper.clamp(height, 32, innerBounds.height - 8); + height -= Math.min((scroll < 0 ? (int) -scroll : scroll > maxScroll ? (int) scroll - maxScroll : 0), height * .95); + height = Math.max(10, height); + int minY = Math.min(Math.max((int) scroll * (innerBounds.height - height) / maxScroll + innerBounds.y, innerBounds.y), innerBounds.getMaxY() - height); + + int scrollbarPositionMinX = getScrollbarMinX(); + int scrollbarPositionMaxX = scrollbarPositionMinX + 6; + boolean hovered = (new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height)).contains(PointHelper.fromMouse()); + float bottomC = (hovered ? .67f : .5f) * (ScreenHelper.isDarkModeEnabled() ? 0.8f : 1f); + float topC = (hovered ? .87f : .67f) * (ScreenHelper.isDarkModeEnabled() ? 0.8f : 1f); + + RenderSystem.disableTexture(); + RenderSystem.enableBlend(); + RenderSystem.disableAlphaTest(); + RenderSystem.blendFuncSeparate(770, 771, 1, 0); + RenderSystem.shadeModel(7425); + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin(7, VertexFormats.POSITION_COLOR); + buffer.vertex(scrollbarPositionMinX, minY + height, 0.0D).color(bottomC, bottomC, bottomC, 1).next(); + buffer.vertex(scrollbarPositionMaxX, minY + height, 0.0D).color(bottomC, bottomC, bottomC, 1).next(); + buffer.vertex(scrollbarPositionMaxX, minY, 0.0D).color(bottomC, bottomC, bottomC, 1).next(); + buffer.vertex(scrollbarPositionMinX, minY, 0.0D).color(bottomC, bottomC, bottomC, 1).next(); + tessellator.draw(); + buffer.begin(7, VertexFormats.POSITION_COLOR); + buffer.vertex(scrollbarPositionMinX, (minY + height - 1), 0.0D).color(topC, topC, topC, 1).next(); + buffer.vertex((scrollbarPositionMaxX - 1), (minY + height - 1), 0.0D).color(topC, topC, topC, 1).next(); + buffer.vertex((scrollbarPositionMaxX - 1), minY, 0.0D).color(topC, topC, topC, 1).next(); + buffer.vertex(scrollbarPositionMinX, minY, 0.0D).color(topC, topC, topC, 1).next(); + tessellator.draw(); + RenderSystem.shadeModel(7424); + RenderSystem.disableBlend(); + RenderSystem.enableAlphaTest(); + RenderSystem.enableTexture(); + } + } + + private void updatePosition(float delta) { + if (ConfigObject.getInstance().doesSnapToRows() && target >= 0 && target <= getMaxScroll()) { + double nearestRow = Math.round(target / (double) entrySize()) * (double) entrySize(); + if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(target, nearestRow, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON)) + target += (nearestRow - target) * Math.min(delta / 2.0, 1.0); + else + target = nearestRow; + } + double[] targetD = new double[]{this.target}; + this.scroll = ClothConfigInitializer.handleScrollingPosition(targetD, this.scroll, this.getMaxScroll(), delta, this.start, this.duration); + this.target = targetD[0]; + } + + public void updateSearch(String searchTerm) { + lastSearchArguments = SearchArgument.processSearchTerm(searchTerm); + List list = Lists.newArrayList(); + for (EntryStack stack : EntryRegistry.getInstance().getStacksList()) { + if (canLastSearchTermsBeAppliedTo(stack)) { + list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.CHECK_TAGS, EntryStack.Settings.TRUE)); + } + } + entryStacks = list; + updateEntriesPosition(); + } + + public boolean canLastSearchTermsBeAppliedTo(EntryStack stack) { + return lastSearchArguments.isEmpty() || SearchArgument.canSearchTermsBeAppliedTo(stack, lastSearchArguments); + } + + public void updateEntriesPosition() { + this.innerBounds = updateInnerBounds(getBounds()); + int width = innerBounds.width / entrySize(); + int pageHeight = innerBounds.height / entrySize(); + int slotsToPrepare = Math.max(entryStacks.size() * 3, width * pageHeight * 3); + int currentX = 0; + int currentY = 0; + List entries = Lists.newArrayList(); + for (int i = 0; i < slotsToPrepare; i++) { + int xPos = currentX * entrySize() + innerBounds.x; + int yPos = currentY * entrySize() + innerBounds.y; + entries.add(new EntryListEntry(xPos, yPos)); + currentX++; + if (currentX >= width) { + currentX = 0; + currentY++; + } + } + this.entries = entries; + this.elements = Lists.newArrayList(entries); + this.elements.add(searchField); + } + + @Override + public List children() { + return elements; + } + + @Override + public boolean mouseClicked(double double_1, double double_2, int int_1) { + double height = getMaxScroll(); + Rectangle bounds = getBounds(); + int actualHeight = bounds.height; + if (height > actualHeight && double_2 >= bounds.y && double_2 <= bounds.getMaxY()) { + double scrollbarPositionMinX = getScrollbarMinX(); + if (double_1 >= scrollbarPositionMinX - 1 & double_1 <= scrollbarPositionMinX + 8) { + this.draggingScrollBar = true; + return true; + } + } + this.draggingScrollBar = false; + + if (bounds.contains(double_1, double_2)) { + if (searchField.mouseClicked(double_1, double_2, int_1)) { + this.selectionPoint = null; + this.secondPoint = null; + return true; + } else if (selectAllButton.mouseClicked(double_1, double_2, int_1)) { + return true; + } else if (selectNoneButton.mouseClicked(double_1, double_2, int_1)) { + return true; + } else if (hideButton.mouseClicked(double_1, double_2, int_1)) { + return true; + } else if (showButton.mouseClicked(double_1, double_2, int_1)) { + return true; + } + if (int_1 == 0) { + this.selectionPoint = new Point(double_1, double_2 + scroll); + this.secondPoint = null; + return true; + } + } + return false; + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (selectionPoint != null && button == 0 && secondPoint == null) { + this.secondPoint = new Point(mouseX, mouseY + scroll); + if (secondPoint.equals(selectionPoint)) { + secondPoint.translate(1, 1); + } + return true; + } + return super.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean charTyped(char chr, int keyCode) { + for (Element element : children()) + if (element.charTyped(chr, keyCode)) + return true; + return super.charTyped(chr, keyCode); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + for (Element element : children()) + if (element.keyPressed(keyCode, scanCode, modifiers)) + return true; + if (Screen.isSelectAll(keyCode)) { + this.selectionPoint = new Point(0, 0); + this.secondPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + return true; + } + return false; + } + + public void updateArea(@Nullable String searchTerm) { + if (searchTerm != null) + updateSearch(searchTerm); + else if (entryStacks == null) + updateSearch(""); + else + updateEntriesPosition(); + } + + private static Rectangle updateInnerBounds(Rectangle bounds) { + int width = Math.max(MathHelper.floor((bounds.width - 2 - 6) / (float) entrySize()), 1); + return new Rectangle((int) (bounds.getCenterX() - width * entrySize() / 2f), bounds.y + 5, width * entrySize(), bounds.height); + } + + protected final int getMaxScrollPosition() { + return MathHelper.ceil(entryStacks.size() / (innerBounds.width / (float) entrySize())) * entrySize() + 28; + } + + protected final int getMaxScroll() { + return Math.max(0, this.getMaxScrollPosition() - innerBounds.height); + } + + protected final double clamp(double v) { + return this.clamp(v, 200.0D); + } + + protected final double clamp(double v, double clampExtension) { + return MathHelper.clamp(v, -clampExtension, (double) this.getMaxScroll() + clampExtension); + } + + protected final void offset(double value, boolean animated) { + scrollTo(target + value, animated); + } + + protected final void scrollTo(double value, boolean animated) { + scrollTo(value, animated, ClothConfigInitializer.getScrollDuration()); + } + + protected final void scrollTo(double value, boolean animated, long duration) { + target = clamp(value); + + if (animated) { + start = System.currentTimeMillis(); + this.duration = duration; + } else + scroll = target; + } + + @Override + public boolean mouseScrolled(double double_1, double double_2, double double_3) { + if (getBounds().contains(double_1, double_2)) { + offset(ClothConfigInitializer.getScrollStep() * -double_3, true); + return true; + } + super.mouseScrolled(double_1, double_2, double_3); + return true; + } + + private class EntryListEntry extends EntryWidget { + private int backupY; + + private EntryListEntry(int x, int y) { + super(new Point(x, y)); + this.backupY = y; + getBounds().width = getBounds().height = entrySize(); + interactableFavorites(false); + interactable(false); + noHighlight(); + } + + @Override + public boolean containsMouse(double mouseX, double mouseY) { + return super.containsMouse(mouseX, mouseY) && FilteringEntry.this.getBounds().contains(mouseX, mouseY); + } + + @Override + protected void drawHighlighted(int mouseX, int mouseY, float delta) { + + } + + @Override + public void render(int mouseX, int mouseY, float delta) { + super.render(mouseX, mouseY, delta); + if (isSelected()) { + Rectangle bounds = getBounds(); + RenderSystem.disableDepthTest(); + fillGradient(bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), 0x896b70fa, 0x896b70fa); + RenderSystem.enableDepthTest(); + } + } + + @Override + public EntryStack getCurrentEntry() { + return super.getCurrentEntry(); + } + + public boolean isSelected() { + return getSelection().intersects(getBounds()); + } + + public boolean isFiltered() { + return CollectionUtils.findFirstOrNullEquals(configFiltered, getCurrentEntry()) != null; + } + + @Override + protected void drawBackground(int mouseX, int mouseY, float delta) { + if (isFiltered()) { + Rectangle bounds = getBounds(); + RenderSystem.disableDepthTest(); + fillGradient(bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), 0xffff0000, 0xffff0000); + RenderSystem.enableDepthTest(); + } + } + + @Override + protected void queueTooltip(int mouseX, int mouseY, float delta) { + if (searchField.containsMouse(mouseX, mouseY)) + return; + QueuedTooltip tooltip = getCurrentTooltip(mouseX, mouseY); + if (tooltip != null) { + FilteringEntry.this.tooltip = tooltip; + } + } + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java b/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java new file mode 100644 index 000000000..0b827c105 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, 2019, 2020 shedaniel + * Licensed under the MIT License (the "License"). + */ + +package me.shedaniel.rei.gui.config.entry; + +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import me.shedaniel.clothconfig2.gui.ClothConfigScreen; +import me.shedaniel.rei.api.EntryStack; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Element; +import net.minecraft.client.resource.language.I18n; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +@ApiStatus.Internal +public class NoFilteringEntry extends AbstractConfigListEntry> { + private Consumer> saveConsumer; + private List defaultValue; + private List configFiltered; + + public NoFilteringEntry(List configFiltered, List defaultValue, Consumer> saveConsumer) { + super("", false); + this.configFiltered = configFiltered; + this.defaultValue = defaultValue; + this.saveConsumer = saveConsumer; + } + + @Override + public List getValue() { + return configFiltered; + } + + @Override + public Optional> getDefaultValue() { + return Optional.ofNullable(defaultValue); + } + + @Override + public void save() { + saveConsumer.accept(getValue()); + } + + @SuppressWarnings("rawtypes") + @Override + public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + ClothConfigScreen.ListWidget parent = getParent(); + drawCenteredString(MinecraftClient.getInstance().textRenderer, I18n.translate("config.roughlyenoughitems.filteredEntries.loadWorldFirst"), (parent.right - parent.left) / 2 + parent.left, (parent.bottom - parent.top) / 2 + parent.top - 5, -1); + } + + @Override + public boolean mouseScrolled(double d, double e, double amount) { + return true; + } + + @Override + public List children() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java b/src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java index c3d327199..7b5ee2dc4 100644 --- a/src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java +++ b/src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java @@ -32,13 +32,14 @@ import net.minecraft.client.util.math.Matrix4f; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemGroup; import net.minecraft.util.ActionResult; -import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; -import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -58,7 +59,6 @@ public class EntryListWidget extends WidgetWithBounds { }); private static final int SIZE = 18; private static final boolean LAZY = true; - private static final String SPACE = " ", EMPTY = ""; private static int page; protected double target; protected double scroll; @@ -620,7 +620,7 @@ public class EntryListWidget extends WidgetWithBounds { } public void updateSearch(String searchTerm) { - lastSearchArguments = processSearchTerm(searchTerm); + lastSearchArguments = SearchArgument.processSearchTerm(searchTerm); { List list = Lists.newArrayList(); boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled() && !ScreenHelper.inventoryStacks.isEmpty(); @@ -631,7 +631,8 @@ public class EntryListWidget extends WidgetWithBounds { if (canLastSearchTermsBeAppliedTo(stack)) { if (workingItems != null && CollectionUtils.findFirstOrNullEquals(workingItems, stack) == null) continue; - list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_ENCHANTMENT_GLINT, RENDER_ENCHANTMENT_GLINT)); + if (CollectionUtils.findFirstOrNullEquals(ConfigObject.getInstance().getFilteredStacks(), stack) == null) + list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_ENCHANTMENT_GLINT, RENDER_ENCHANTMENT_GLINT)); } } } @@ -673,118 +674,7 @@ public class EntryListWidget extends WidgetWithBounds { } public boolean canLastSearchTermsBeAppliedTo(EntryStack stack) { - return lastSearchArguments.isEmpty() || canSearchTermsBeAppliedTo(stack, lastSearchArguments); - } - - private boolean canSearchTermsBeAppliedTo(EntryStack stack, List searchArguments) { - if (searchArguments.isEmpty()) - return true; - String mod = null; - String modName = null; - String name = null; - String tooltip = null; - String[] tags = null; - for (SearchArgument.SearchArguments arguments : searchArguments) { - boolean applicable = true; - for (SearchArgument argument : arguments.getArguments()) { - if (argument.getArgumentType() == SearchArgument.ArgumentType.ALWAYS) - return true; - else if (argument.getArgumentType() == SearchArgument.ArgumentType.MOD) { - if (mod == null) - mod = stack.getIdentifier().map(Identifier::getNamespace).orElse("").replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); - if (mod != null && !mod.isEmpty()) { - if (argument.getFunction(!argument.isInclude()).apply(mod)) { - if (modName == null) - modName = ClientHelper.getInstance().getModFromModId(mod).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); - if (modName == null || modName.isEmpty() || argument.getFunction(!argument.isInclude()).apply(modName)) { - applicable = false; - break; - } - break; - } - } - } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TEXT) { - if (name == null) - name = SearchArgument.tryGetEntryStackName(stack).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); - if (name != null && !name.isEmpty() && argument.getFunction(!argument.isInclude()).apply(name)) { - applicable = false; - break; - } - } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TOOLTIP) { - if (name == null) - name = SearchArgument.tryGetEntryStackTooltip(stack).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); - if (name != null && !name.isEmpty() && argument.getFunction(!argument.isInclude()).apply(name)) { - applicable = false; - break; - } - } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TAG) { - if (tags == null) { - if (stack.getType() == EntryStack.Type.ITEM) { - Identifier[] tagsFor = minecraft.getNetworkHandler().getTagManager().items().getTagsFor(stack.getItem()).toArray(new Identifier[0]); - tags = new String[tagsFor.length]; - for (int i = 0; i < tagsFor.length; i++) - tags[i] = tagsFor[i].toString(); - } else if (stack.getType() == EntryStack.Type.FLUID) { - Identifier[] tagsFor = minecraft.getNetworkHandler().getTagManager().fluids().getTagsFor(stack.getFluid()).toArray(new Identifier[0]); - tags = new String[tagsFor.length]; - for (int i = 0; i < tagsFor.length; i++) - tags[i] = tagsFor[i].toString(); - } else - tags = new String[0]; - } - if (tags != null && tags.length > 0) { - boolean a = false; - for (String tag : tags) - if (argument.getFunction(argument.isInclude()).apply(tag)) - a = true; - if (!a) { - applicable = false; - break; - } - } else { - applicable = false; - break; - } - } - } - if (applicable) - return true; - } - return false; - } - - private List processSearchTerm(String searchTerm) { - List searchArguments = Lists.newArrayList(); - for (String split : StringUtils.splitByWholeSeparatorPreserveAllTokens(searchTerm.toLowerCase(Locale.ROOT), "|")) { - String[] terms = StringUtils.split(split); - if (terms.length == 0) - searchArguments.add(SearchArgument.SearchArguments.ALWAYS); - else { - SearchArgument[] arguments = new SearchArgument[terms.length]; - for (int i = 0; i < terms.length; i++) { - String term = terms[i]; - if (term.startsWith("-@") || term.startsWith("@-")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.MOD, term.substring(2), false); - } else if (term.startsWith("@")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.MOD, term.substring(1), true); - } else if (term.startsWith("-$") || term.startsWith("$-")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TAG, term.substring(2), false); - } else if (term.startsWith("$")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TAG, term.substring(1), true); - } else if (term.startsWith("-#") || term.startsWith("#-")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TOOLTIP, term.substring(2), false); - } else if (term.startsWith("#")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TOOLTIP, term.substring(1), true); - } else if (term.startsWith("-")) { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TEXT, term.substring(1), false); - } else { - arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TEXT, term, true); - } - } - searchArguments.add(new SearchArgument.SearchArguments(arguments)); - } - } - return searchArguments; + return lastSearchArguments.isEmpty() || SearchArgument.canSearchTermsBeAppliedTo(stack, lastSearchArguments); } @Override diff --git a/src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java b/src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java index 17d961d06..9e4965f86 100644 --- a/src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java +++ b/src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java @@ -85,7 +85,7 @@ public class FavoritesListWidget extends WidgetWithBounds { @Override public boolean mouseScrolled(double double_1, double double_2, double double_3) { - if (ConfigObject.getInstance().isEntryListWidgetScrolled() && bounds.contains(double_1, double_2)) { + if (bounds.contains(double_1, double_2)) { offset(ClothConfigInitializer.getScrollStep() * -double_3, true); return true; } diff --git a/src/main/java/me/shedaniel/rei/gui/widget/TextFieldWidget.java b/src/main/java/me/shedaniel/rei/gui/widget/TextFieldWidget.java index c83fa66a3..783d2d9ea 100644 --- a/src/main/java/me/shedaniel/rei/gui/widget/TextFieldWidget.java +++ b/src/main/java/me/shedaniel/rei/gui/widget/TextFieldWidget.java @@ -483,18 +483,6 @@ public class TextFieldWidget extends WidgetWithBounds implements Tickable { int b = (color & 255); Tessellator tessellator = Tessellator.getInstance(); BufferBuilder buffer = tessellator.getBuffer(); - // RenderSystem.color4f(0.0F, 0.0F, 255.0F, 255.0F); - // RenderSystem.disableTexture(); - // RenderSystem.enableColorLogicOp(); - // RenderSystem.logicOp(GlStateManager.LogicOp.OR_REVERSE); - // buffer.begin(7, VertexFormats.POSITION); - // buffer.vertex(x1, y2, getBlitOffset() + 50d).next(); - // buffer.vertex(x2, y2, getBlitOffset() + 50d).next(); - // buffer.vertex(x2, y1, getBlitOffset() + 50d).next(); - // buffer.vertex(x1, y1, getBlitOffset() + 50d).next(); - // tessellator.draw(); - // RenderSystem.disableColorLogicOp(); - // RenderSystem.enableTexture(); RenderSystem.disableTexture(); RenderSystem.enableBlend(); RenderSystem.disableAlphaTest(); diff --git a/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java b/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java index e201fd7b7..f0c376053 100644 --- a/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java +++ b/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java @@ -33,6 +33,8 @@ import me.shedaniel.rei.api.RecipeHelper; import me.shedaniel.rei.gui.ConfigReloadingScreen; import me.shedaniel.rei.gui.ContainerScreenOverlay; import me.shedaniel.rei.gui.config.RecipeScreenType; +import me.shedaniel.rei.gui.config.entry.FilteringEntry; +import me.shedaniel.rei.gui.config.entry.NoFilteringEntry; import me.shedaniel.rei.gui.config.entry.RecipeScreenTypeEntry; import me.shedaniel.rei.gui.credits.CreditsScreen; import net.fabricmc.loader.api.FabricLoader; @@ -48,6 +50,7 @@ import net.minecraft.client.util.Window; import net.minecraft.util.math.MathHelper; import org.jetbrains.annotations.ApiStatus; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -99,6 +102,12 @@ public class ConfigManagerImpl implements ConfigManager { guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> Collections.singletonList(new RecipeScreenTypeEntry(220, i13n, getUnsafely(field, config, RecipeScreenType.UNSET), getUnsafely(field, defaults), type -> setUnsafely(field, config, type))) , (field) -> field.getType() == RecipeScreenType.class, ConfigObjectImpl.UseSpecialRecipeTypeScreen.class); + guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> + ScreenHelper.getLastContainerScreenHooks() == null || MinecraftClient.getInstance().getNetworkHandler() == null || MinecraftClient.getInstance().getNetworkHandler().getRecipeManager() == null ? + Collections.singletonList(new NoFilteringEntry(getUnsafely(field, config, new ArrayList<>()), getUnsafely(field, defaults), list -> setUnsafely(field, config, list))) + : + Collections.singletonList(new FilteringEntry(getUnsafely(field, config, new ArrayList<>()), getUnsafely(field, defaults), list -> setUnsafely(field, config, list))) + , (field) -> field.getType() == List.class, ConfigObjectImpl.UseFilteringScreen.class); saveConfig(); RoughlyEnoughItemsCore.LOGGER.info("[REI] Config is loaded."); } diff --git a/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java b/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java index 98e2c53e9..caa9a6356 100644 --- a/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java +++ b/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java @@ -24,6 +24,7 @@ import java.util.concurrent.CopyOnWriteArrayList; @ApiStatus.Internal public class EntryRegistryImpl implements EntryRegistry { + private final CopyOnWriteArrayList allEntries = Lists.newCopyOnWriteArrayList(); private final CopyOnWriteArrayList entries = Lists.newCopyOnWriteArrayList(); private final Queue>> queueRegisterEntryStackAfter = Queues.newConcurrentLinkedQueue(); private List reloadList; @@ -49,9 +50,6 @@ public class EntryRegistryImpl implements EntryRegistry { entries.clear(); entries.addAll(set); set.clear(); - for (EntryStack stack : ConfigObject.getInstance().getFilteredStacks()) { - entries.remove(stack); - } doingDistinct = false; } diff --git a/src/main/java/me/shedaniel/rei/impl/SearchArgument.java b/src/main/java/me/shedaniel/rei/impl/SearchArgument.java index e3ebd48aa..2ccc0010e 100644 --- a/src/main/java/me/shedaniel/rei/impl/SearchArgument.java +++ b/src/main/java/me/shedaniel/rei/impl/SearchArgument.java @@ -6,6 +6,7 @@ package me.shedaniel.rei.impl; import com.google.common.collect.Lists; +import me.shedaniel.rei.api.ClientHelper; import me.shedaniel.rei.api.EntryStack; import me.shedaniel.rei.gui.widget.QueuedTooltip; import me.shedaniel.rei.utils.CollectionUtils; @@ -29,6 +30,7 @@ import java.util.function.Function; @ApiStatus.Internal public class SearchArgument { + private static final String SPACE = " ", EMPTY = ""; private static final SearchArgument ALWAYS = new SearchArgument(ArgumentType.ALWAYS, "", true); private static List searchBlacklisted = Lists.newArrayList(); private ArgumentType argumentType; @@ -47,6 +49,120 @@ public class SearchArgument { this.include = include; } + @ApiStatus.Internal + public static List processSearchTerm(String searchTerm) { + List searchArguments = Lists.newArrayList(); + for (String split : StringUtils.splitByWholeSeparatorPreserveAllTokens(searchTerm.toLowerCase(Locale.ROOT), "|")) { + String[] terms = StringUtils.split(split); + if (terms.length == 0) + searchArguments.add(SearchArgument.SearchArguments.ALWAYS); + else { + SearchArgument[] arguments = new SearchArgument[terms.length]; + for (int i = 0; i < terms.length; i++) { + String term = terms[i]; + if (term.startsWith("-@") || term.startsWith("@-")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.MOD, term.substring(2), false); + } else if (term.startsWith("@")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.MOD, term.substring(1), true); + } else if (term.startsWith("-$") || term.startsWith("$-")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TAG, term.substring(2), false); + } else if (term.startsWith("$")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TAG, term.substring(1), true); + } else if (term.startsWith("-#") || term.startsWith("#-")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TOOLTIP, term.substring(2), false); + } else if (term.startsWith("#")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TOOLTIP, term.substring(1), true); + } else if (term.startsWith("-")) { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TEXT, term.substring(1), false); + } else { + arguments[i] = new SearchArgument(SearchArgument.ArgumentType.TEXT, term, true); + } + } + searchArguments.add(new SearchArgument.SearchArguments(arguments)); + } + } + return searchArguments; + } + + @ApiStatus.Internal + public static boolean canSearchTermsBeAppliedTo(EntryStack stack, List searchArguments) { + if (searchArguments.isEmpty()) + return true; + MinecraftClient minecraft = MinecraftClient.getInstance(); + String mod = null; + String modName = null; + String name = null; + String tooltip = null; + String[] tags = null; + for (SearchArgument.SearchArguments arguments : searchArguments) { + boolean applicable = true; + for (SearchArgument argument : arguments.getArguments()) { + if (argument.getArgumentType() == SearchArgument.ArgumentType.ALWAYS) + return true; + else if (argument.getArgumentType() == SearchArgument.ArgumentType.MOD) { + if (mod == null) + mod = stack.getIdentifier().map(Identifier::getNamespace).orElse("").replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); + if (mod != null && !mod.isEmpty()) { + if (argument.getFunction(!argument.isInclude()).apply(mod)) { + if (modName == null) + modName = ClientHelper.getInstance().getModFromModId(mod).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); + if (modName == null || modName.isEmpty() || argument.getFunction(!argument.isInclude()).apply(modName)) { + applicable = false; + break; + } + break; + } + } + } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TEXT) { + if (name == null) + name = SearchArgument.tryGetEntryStackName(stack).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); + if (name != null && !name.isEmpty() && argument.getFunction(!argument.isInclude()).apply(name)) { + applicable = false; + break; + } + } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TOOLTIP) { + if (name == null) + name = SearchArgument.tryGetEntryStackTooltip(stack).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT); + if (name != null && !name.isEmpty() && argument.getFunction(!argument.isInclude()).apply(name)) { + applicable = false; + break; + } + } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TAG) { + if (tags == null) { + if (stack.getType() == EntryStack.Type.ITEM) { + Identifier[] tagsFor = minecraft.getNetworkHandler().getTagManager().items().getTagsFor(stack.getItem()).toArray(new Identifier[0]); + tags = new String[tagsFor.length]; + for (int i = 0; i < tagsFor.length; i++) + tags[i] = tagsFor[i].toString(); + } else if (stack.getType() == EntryStack.Type.FLUID) { + Identifier[] tagsFor = minecraft.getNetworkHandler().getTagManager().fluids().getTagsFor(stack.getFluid()).toArray(new Identifier[0]); + tags = new String[tagsFor.length]; + for (int i = 0; i < tagsFor.length; i++) + tags[i] = tagsFor[i].toString(); + } else + tags = new String[0]; + } + if (tags != null && tags.length > 0) { + boolean a = false; + for (String tag : tags) + if (argument.getFunction(argument.isInclude()).apply(tag)) + a = true; + if (!a) { + applicable = false; + break; + } + } else { + applicable = false; + break; + } + } + } + if (applicable) + return true; + } + return false; + } + public static String tryGetEntryStackName(EntryStack stack) { if (stack.getType() == EntryStack.Type.ITEM) return tryGetItemStackName(stack.getItemStack()); -- cgit