From bde9a2094747efae09a4ca144297b49d9f882bd1 Mon Sep 17 00:00:00 2001 From: slprime Date: Tue, 24 Dec 2024 18:59:53 +0200 Subject: [PATCH] Subset Widget Improvement --- .../java/codechicken/nei/BookmarkPanel.java | 15 + .../codechicken/nei/CollapsibleItems.java | 20 +- src/main/java/codechicken/nei/ItemList.java | 91 +- src/main/java/codechicken/nei/ItemPanel.java | 74 +- src/main/java/codechicken/nei/ItemSorter.java | 17 +- .../java/codechicken/nei/ItemStackMap.java | 24 +- .../java/codechicken/nei/ItemStackSet.java | 5 + .../java/codechicken/nei/LayoutManager.java | 176 ++-- .../codechicken/nei/LayoutStyleMinecraft.java | 255 ++++-- .../java/codechicken/nei/NEIClientConfig.java | 31 +- .../java/codechicken/nei/SubsetWidget.java | 781 ++++++++++-------- .../java/codechicken/nei/WidgetZOrder.java | 2 +- .../java/codechicken/nei/api/ItemInfo.java | 2 - .../nei/guihook/GuiContainerManager.java | 10 +- .../codechicken/nei/search/TooltipFilter.java | 15 +- src/main/resources/assets/nei/lang/en_US.lang | 24 + 16 files changed, 887 insertions(+), 655 deletions(-) diff --git a/src/main/java/codechicken/nei/BookmarkPanel.java b/src/main/java/codechicken/nei/BookmarkPanel.java index a951b2e8f..7901fa767 100644 --- a/src/main/java/codechicken/nei/BookmarkPanel.java +++ b/src/main/java/codechicken/nei/BookmarkPanel.java @@ -2584,6 +2584,21 @@ private List craftingChainTooltip(int groupId, List currenttip) return currenttip; } + @Override + public boolean contains(int px, int py) { + + if (new Rectangle4i(pagePrev.x + pagePrev.w, pagePrev.y, pageNext.x - (pagePrev.x + pagePrev.w), pagePrev.h) + .contains(px, py)) { + return true; + } + + if (((BookmarkGrid) grid).getHoveredRowIndex(true) != -1) { + return true; + } + + return super.contains(px, py); + } + @Override public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, List currenttip) { diff --git a/src/main/java/codechicken/nei/CollapsibleItems.java b/src/main/java/codechicken/nei/CollapsibleItems.java index cbbe9d943..2046764b4 100644 --- a/src/main/java/codechicken/nei/CollapsibleItems.java +++ b/src/main/java/codechicken/nei/CollapsibleItems.java @@ -145,24 +145,16 @@ public static ItemFilter getItemFilter() { return filter; } - public static void updateCache(final List items) { + public static void clearCache() { CollapsibleItems.cache.clear(); + } - try { - - ItemList.forkJoinPool.submit(() -> items.parallelStream().forEach(stack -> { - GroupItem group = CollapsibleItems.groups.stream().filter(g -> g.matches(stack)).findFirst() - .orElse(null); + public static void putItem(ItemStack stack) { + final GroupItem group = CollapsibleItems.groups.stream().filter(g -> g.matches(stack)).findFirst().orElse(null); - if (group != null) { - CollapsibleItems.cache.put(stack, CollapsibleItems.groups.indexOf(group)); - } - })).get(); - - } catch (Exception e) { - NEIClientConfig.logger.error("Error create collapsible items groups", e); + if (group != null) { + CollapsibleItems.cache.put(stack, CollapsibleItems.groups.indexOf(group)); } - } public static int getGroupIndex(ItemStack stack) { diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index 4044ba658..6b96d6f93 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -11,6 +11,7 @@ import java.util.concurrent.ForkJoinWorkerThread; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import net.minecraft.client.Minecraft; import net.minecraft.item.Item; @@ -25,6 +26,7 @@ import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemFilter.ItemFilterProvider; import codechicken.nei.api.ItemInfo; +import codechicken.nei.search.TooltipFilter; public class ItemList { @@ -265,50 +267,84 @@ private void updateOrdering(List items) { } } + private List getPermutations(Item item) { + final List permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item)); + + if (permutations.isEmpty()) { + item.getSubItems(item, null, permutations); + } + + if (permutations.isEmpty()) { + damageSearch(item, permutations); + } + + permutations.addAll(ItemInfo.itemVariants.get(item)); + + return permutations.stream() + .filter( + stack -> stack.getItem() != null && stack.getItem().delegate.name() != null + && !ItemInfo.isHidden(stack)) + .collect(Collectors.toCollection(ArrayList::new)); + } + + // For optimization it generate itemslist, permutations, orders & collapsibleitems @Override @SuppressWarnings("unchecked") public void execute() { + if (!NEIClientConfig.isEnabled()) return; + ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout()); LayoutManager.itemsLoaded = true; loadFinished = false; - List items = new LinkedList<>(); - List permutations = new LinkedList<>(); + SearchField.searchParser.clearCache(); + ItemSorter.instance.ordering.clear(); + CollapsibleItems.clearCache(); + TooltipFilter.clearCache(); + + List items = new ArrayList<>(); ListMultimap itemMap = ArrayListMultimap.create(); + ItemStackSet unique = new ItemStackSet(); timer.setLimit(NEIClientConfig.getItemLoadingTimeout()); - for (Item item : (Iterable) Item.itemRegistry) { - if (interrupted()) return; - - if (item == null || erroredItems.contains(item)) continue; + StreamSupport.stream(((Iterable) Item.itemRegistry).spliterator(), true).forEach(item -> { + if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return; try { timer.reset(item); + List permutations = getPermutations(item); + timer.reset(); - permutations.clear(); - permutations.addAll(ItemInfo.itemOverrides.get(item)); - - if (permutations.isEmpty()) { - item.getSubItems(item, null, permutations); - } - - if (permutations.isEmpty()) { - damageSearch(item, permutations); - } + for (ItemStack stack : permutations) { + if (!unique.contains(stack)) { - permutations.addAll(ItemInfo.itemVariants.get(item)); + synchronized (unique) { + unique.add(stack); + } - timer.reset(); + synchronized (items) { + items.add(stack); + } - permutations = permutations.stream().filter(stack -> !ItemInfo.isHidden(stack)) - .collect(Collectors.toCollection(ArrayList::new)); + CollapsibleItems.putItem(stack); + } + } - items.addAll(permutations); - itemMap.putAll(item, permutations); + synchronized (itemMap) { + itemMap.putAll(item, permutations); + } } catch (Throwable t) { - NEIServerConfig.logger.error("Removing item: " + item + " from list.", t); + NEIServerConfig.logger.error("Removing item: {} from list.", item, t); erroredItems.add(item); } + + }); + + int index = 0; + for (Item item : (Iterable) Item.itemRegistry) { + for (ItemStack stack : itemMap.get(item)) { + ItemSorter.instance.ordering.put(stack, index++); + } } if (interrupted()) return; @@ -317,10 +353,15 @@ public void execute() { for (ItemsLoadedCallback callback : loadCallbacks) callback.itemsLoaded(); if (interrupted()) return; - CollapsibleItems.updateCache(items); - updateOrdering(items); + updateOrdering(ItemList.items); + + new Thread( + () -> ItemList.items.parallelStream().forEach(TooltipFilter::getSearchTooltip), + "NEI Tooltip Filter Loader").start(); loadFinished = true; + + SubsetWidget.updateHiddenItems(); updateFilter.restart(); } }; diff --git a/src/main/java/codechicken/nei/ItemPanel.java b/src/main/java/codechicken/nei/ItemPanel.java index aa0a065ac..b04b58343 100644 --- a/src/main/java/codechicken/nei/ItemPanel.java +++ b/src/main/java/codechicken/nei/ItemPanel.java @@ -41,9 +41,6 @@ public ItemStack getStackMouseOver(int mousex, int mousey) { return super.getStackMouseOver(mousex, mousey); } - public Button more; - public Button less; - public ItemQuantityField quantity; public ItemHistoryPanel historyPanel; public Button toggleGroups; @@ -345,42 +342,6 @@ public boolean onButtonPress(boolean rightclick) { } }; - more = new Button("+") { - - @Override - public boolean onButtonPress(boolean rightclick) { - if (rightclick) return false; - - int modifier = NEIClientUtils.controlKey() ? 64 : NEIClientUtils.shiftKey() ? 10 : 1; - int quantity = NEIClientConfig.getItemQuantity() + modifier; - - if (quantity < 0) { - quantity = 0; - } - - ItemPanels.itemPanel.quantity.setText(Integer.toString(quantity)); - return true; - } - }; - less = new Button("-") { - - @Override - public boolean onButtonPress(boolean rightclick) { - if (rightclick) return false; - - int modifier = NEIClientUtils.controlKey() ? -64 : NEIClientUtils.shiftKey() ? -10 : -1; - int quantity = NEIClientConfig.getItemQuantity() + modifier; - - if (quantity < 0) { - quantity = 0; - } - - ItemPanels.itemPanel.quantity.setText(Integer.toString(quantity)); - return true; - } - }; - - quantity = new ItemQuantityField("quantity"); historyPanel = new ItemHistoryPanel(); } @@ -431,44 +392,21 @@ protected int resizeFooter(GuiContainer gui) { return 0; } - final int BUTTON_SIZE = 20; - more.w = less.w = BUTTON_SIZE; - quantity.h = BUTTON_SIZE; - - if (NEIClientConfig.isSearchWidgetCentered()) { - more.h = less.h = BUTTON_SIZE; - more.x = x + w - BUTTON_SIZE; - more.y = less.y = quantity.y = y + h - BUTTON_SIZE; - less.x = x; - quantity.x = x + BUTTON_SIZE + 2; - quantity.w = more.x - quantity.x - 2; - } else { - quantity.x = (int) (x + (w * 0.7)) + 3; - quantity.y = y + h - BUTTON_SIZE; - quantity.w = (int) ((w * 0.3) - BUTTON_SIZE - 1); - - more.h = less.h = BUTTON_SIZE / 2; - more.y = y + h - (more.h * 2); - - less.x = more.x = quantity.x + quantity.w; - less.y = more.y + more.h; - } - if (NEIClientConfig.showHistoryPanelWidget()) { historyPanel.x = x; historyPanel.w = w; historyPanel.h = ItemsGrid.SLOT_SIZE * NEIClientConfig.getIntSetting("inventory.history.useRows"); if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) { - historyPanel.y = quantity.y - historyPanel.h - PanelWidget.PADDING; - return quantity.h + historyPanel.h + PanelWidget.PADDING * 2; + historyPanel.y = LayoutManager.quantity.y - historyPanel.h - PanelWidget.PADDING; + return LayoutManager.quantity.h + historyPanel.h + PanelWidget.PADDING * 2; } else { historyPanel.y = y + h - historyPanel.h; return historyPanel.h + PanelWidget.PADDING; } } - return quantity.h + PanelWidget.PADDING; + return LayoutManager.quantity.h + PanelWidget.PADDING; } @Override @@ -476,12 +414,6 @@ public void setVisible() { super.setVisible(); if (grid.getPerPage() > 0) { - if (NEIClientConfig.showItemQuantityWidget()) { - LayoutManager.addWidget(more); - LayoutManager.addWidget(less); - LayoutManager.addWidget(quantity); - } - if (!CollapsibleItems.isEmpty()) { LayoutManager.addWidget(toggleGroups); } diff --git a/src/main/java/codechicken/nei/ItemSorter.java b/src/main/java/codechicken/nei/ItemSorter.java index 13f7ab9cf..6bf2e95a9 100644 --- a/src/main/java/codechicken/nei/ItemSorter.java +++ b/src/main/java/codechicken/nei/ItemSorter.java @@ -4,19 +4,19 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Map; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.StatCollector; import codechicken.lib.config.ConfigTagParent; -import codechicken.nei.ItemList.ItemsLoadedCallback; import codechicken.nei.api.API; import codechicken.nei.api.ItemInfo; import codechicken.nei.config.GuiItemSorter; import codechicken.nei.config.OptionOpenGui; -public class ItemSorter implements Comparator, ItemsLoadedCallback { +public class ItemSorter implements Comparator { public static class SortEntry { @@ -44,12 +44,10 @@ public String getTooltip() { public static final ItemSorter instance = new ItemSorter(); // optimisations - public HashMap ordering = null; + public Map ordering = new HashMap<>(); public static void sort(List items) { try { - // items = (ArrayList) - // items.parallelStream().sorted(instance).collect(Collectors.toList()); items.sort(instance); } catch (Exception e) { NEIClientConfig.logger.error("Exception sorting item list", e); @@ -65,14 +63,6 @@ public int compare(ItemStack o1, ItemStack o2) { return 0; } - @Override - public void itemsLoaded() { - HashMap newMap = new HashMap<>(); - int i = 0; - for (ItemStack stack : ItemList.items) newMap.put(stack, i++); - ordering = newMap; - } - public static SortEntry find(String name) { for (SortEntry e : entries) if (e.name.equals(name)) return e; @@ -136,7 +126,6 @@ public void useGlobals() { list = fromSaveString(activeTag().getValue()); } }); - ItemList.loadCallbacks.add(instance); } public static String getSaveString(List list) { diff --git a/src/main/java/codechicken/nei/ItemStackMap.java b/src/main/java/codechicken/nei/ItemStackMap.java index 391a89296..8c6b54305 100644 --- a/src/main/java/codechicken/nei/ItemStackMap.java +++ b/src/main/java/codechicken/nei/ItemStackMap.java @@ -1,10 +1,10 @@ package codechicken.nei; -import static codechicken.lib.inventory.InventoryUtils.actualDamage; import static codechicken.lib.inventory.InventoryUtils.newItemStack; import static net.minecraftforge.oredict.OreDictionary.WILDCARD_VALUE; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -24,18 +24,23 @@ public static class StackMetaKey { public final int damage; public final NBTTagCompound tag; + private final int hashCode; public StackMetaKey(int damage, NBTTagCompound tag) { + if (tag != null && tag.hasNoTags()) { + tag = null; + } + this.hashCode = Objects.hashCode(damage, tag); this.damage = damage; this.tag = tag; } public StackMetaKey(ItemStack key) { - this(actualDamage(key), key.stackTagCompound); + this(key.getItemDamage(), key.stackTagCompound); } public int hashCode() { - return Objects.hashCode(damage, tag); + return this.hashCode; } public boolean equals(Object o) { @@ -69,7 +74,7 @@ public T get(ItemStack key) { if (wildcard != null) return wildcard; if (damageMap != null) { - final T ret = damageMap.get(actualDamage(key)); + final T ret = damageMap.get(key.getItemDamage()); if (ret != null) return ret; } if (tagMap != null) { @@ -83,7 +88,7 @@ public T get(ItemStack key) { public T put(ItemStack key, T value) { try { - switch (getKeyType(actualDamage(key), key.stackTagCompound)) { + switch (getKeyType(key.getItemDamage(), key.stackTagCompound)) { case 0: if (metaMap == null) metaMap = new HashMap<>(); return metaMap.put(new StackMetaKey(key), value); @@ -92,7 +97,7 @@ public T put(ItemStack key, T value) { return tagMap.put(key.stackTagCompound, value); case 2: if (damageMap == null) damageMap = new HashMap<>(); - return damageMap.put(actualDamage(key), value); + return damageMap.put(key.getItemDamage(), value); case 3: T ret = wildcard; wildcard = value; @@ -107,13 +112,13 @@ public T put(ItemStack key, T value) { public T remove(ItemStack key) { try { - switch (getKeyType(actualDamage(key), key.stackTagCompound)) { + switch (getKeyType(key.getItemDamage(), key.stackTagCompound)) { case 0: return metaMap != null ? metaMap.remove(new StackMetaKey(key)) : null; case 1: return tagMap != null ? tagMap.remove(key.stackTagCompound) : null; case 2: - return damageMap != null ? damageMap.remove(actualDamage(key)) : null; + return damageMap != null ? damageMap.remove(key.getItemDamage()) : null; case 3: T ret = wildcard; wildcard = null; @@ -191,7 +196,7 @@ public static boolean isWildcard(NBTTagCompound tag) { WILDCARD_TAG.setBoolean("*", true); } - private final HashMap itemMap = new HashMap<>(); + private final Map itemMap = new LinkedHashMap<>(); private int size; public T get(ItemStack key) { @@ -218,6 +223,7 @@ public void put(ItemStack key, T value) { public void clear() { itemMap.clear(); + size = 0; } public T remove(ItemStack key) { diff --git a/src/main/java/codechicken/nei/ItemStackSet.java b/src/main/java/codechicken/nei/ItemStackSet.java index e11a3cc92..47e98d302 100644 --- a/src/main/java/codechicken/nei/ItemStackSet.java +++ b/src/main/java/codechicken/nei/ItemStackSet.java @@ -32,6 +32,11 @@ public ItemStackSet addAll(Iterable items) { return this; } + public ItemStackSet removeAll(Iterable items) { + for (ItemStack item : items) remove(item); + return this; + } + public boolean contains(ItemStack item) { return get(item) != null; } diff --git a/src/main/java/codechicken/nei/LayoutManager.java b/src/main/java/codechicken/nei/LayoutManager.java index f0950b1bc..08b9fa8a4 100644 --- a/src/main/java/codechicken/nei/LayoutManager.java +++ b/src/main/java/codechicken/nei/LayoutManager.java @@ -12,7 +12,6 @@ import static codechicken.nei.NEIClientConfig.isBookmarkPanelHidden; import static codechicken.nei.NEIClientConfig.isEnabled; import static codechicken.nei.NEIClientConfig.isHidden; -import static codechicken.nei.NEIClientConfig.isLoaded; import static codechicken.nei.NEIClientConfig.showIDs; import static codechicken.nei.NEIClientConfig.toggleBooleanSetting; import static codechicken.nei.NEIClientUtils.cycleGamemode; @@ -32,10 +31,11 @@ import static codechicken.nei.NEIClientUtils.toggleRaining; import static codechicken.nei.NEIClientUtils.translate; +import java.awt.Point; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeSet; import net.minecraft.client.Minecraft; @@ -79,13 +79,11 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH /** * Sorted bottom first */ - private static TreeSet drawWidgets = new TreeSet<>(new WidgetZOrder(false)); + private static Set drawWidgets = new TreeSet<>(new WidgetZOrder(false)); /** * Sorted top first */ - private static TreeSet controlWidgets = new TreeSet<>(new WidgetZOrder(true)); - - private static List tooltipWidgets = new LinkedList<>(); + private static Set controlWidgets = new TreeSet<>(new WidgetZOrder(true)); public static ItemPanel itemPanel; public static BookmarkPanel bookmarkPanel; @@ -96,13 +94,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH public static ButtonCycled options; public static ButtonCycled bookmarksButton; - @Deprecated public static Button more; - - @Deprecated public static Button less; - - @Deprecated public static ItemQuantityField quantity; public static Button delete; @@ -150,8 +143,8 @@ public void onPreDraw(GuiContainer gui) { gui.guiTop = (gui.height - gui.ySize) / 2; if (gui instanceof GuiContainerCreative && gui.buttonList.size() >= 2) { - GuiButton button1 = (GuiButton) gui.buttonList.get(0); - GuiButton button2 = (GuiButton) gui.buttonList.get(1); + GuiButton button1 = gui.buttonList.get(0); + GuiButton button2 = gui.buttonList.get(1); button1.xPosition = gui.guiLeft; button2.xPosition = gui.guiLeft + gui.xSize - 20; } @@ -172,14 +165,18 @@ public static int getLeftSize(GuiContainer gui) { public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) { if (isHidden()) return; - for (Widget widget : controlWidgets) widget.onGuiClick(mousex, mousey); + for (Widget widget : controlWidgets) { + widget.onGuiClick(mousex, mousey); + } } @Override public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) { if (isHidden()) return false; - if (!isEnabled()) return options.contains(mousex, mousey) && options.handleClick(mousex, mousey, button); + if (!isEnabled()) { + return options.contains(mousex, mousey) && options.handleClick(mousex, mousey, button); + } for (Widget widget : controlWidgets) { widget.onGuiClick(mousex, mousey); @@ -193,10 +190,22 @@ public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button @Override public boolean objectUnderMouse(GuiContainer gui, int mousex, int mousey) { - if (!isHidden() && isEnabled()) - for (Widget widget : drawWidgets) if (widget.contains(mousex, mousey)) return true; + return getWidgetUnderMouse(mousex, mousey) != null; + } - return false; + public Widget getWidgetUnderMouse(int mousex, int mousey) { + + if (isHidden() || !isEnabled()) { + return null; + } + + for (Widget widget : controlWidgets) { + if (widget.contains(mousex, mousey)) { + return widget; + } + } + + return null; } public boolean keyTyped(GuiContainer gui, char keyChar, int keyID) { @@ -252,12 +261,12 @@ public void onMouseDragged(GuiContainer gui, int mx, int my, int button, long he @Override public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) { - if (!isHidden() && isEnabled()) { - for (Widget widget : drawWidgets) { - ItemStack stack = widget.getStackMouseOver(mousex, mousey); - if (stack != null) return stack; - } + final Widget focused = getWidgetUnderMouse(mousex, mousey); + + if (focused != null) { + return focused.getStackMouseOver(mousex, mousey); } + return null; } @@ -286,18 +295,32 @@ public void postRenderObjects(GuiContainer gui, int mousex, int mousey) { @Override public void postRenderTooltips(GuiContainer gui, int mousex, int mousey, List tooltip) { + if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { - for (Widget widget : drawWidgets) widget.postDrawTooltips(mousex, mousey, tooltip); + final Widget focused = getWidgetUnderMouse(mousex, mousey); + + if (focused != null) { + focused.postDrawTooltips(mousex, mousey, tooltip); + } } + } @Override public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { + if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { - for (Widget widget : drawWidgets) currenttip = widget.handleTooltip(mousex, mousey, currenttip); - for (IContainerTooltipHandler tip : tooltipWidgets) - currenttip = tip.handleTooltip(gui, mousex, mousey, currenttip); + final Widget focused = getWidgetUnderMouse(mousex, mousey); + + if (focused != null) { + currenttip = focused.handleTooltip(mousex, mousey, currenttip); + + if (focused instanceof IContainerTooltipHandler tip) { + currenttip = tip.handleTooltip(gui, mousex, mousey, currenttip); + } + } } + return currenttip; } @@ -315,8 +338,12 @@ public List handleItemDisplayName(GuiContainer gui, ItemStack stack, Lis } if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { - for (IContainerTooltipHandler tip : tooltipWidgets) + final Point mouse = GuiDraw.getMousePosition(); + final Widget focused = getWidgetUnderMouse(mouse.x, mouse.y); + + if (focused instanceof IContainerTooltipHandler tip) { currenttip = tip.handleItemDisplayName(gui, stack, currenttip); + } } return currenttip; @@ -327,8 +354,11 @@ public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int List currenttip) { if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { - for (IContainerTooltipHandler tip : tooltipWidgets) + final Widget focused = getWidgetUnderMouse(mousex, mousey); + + if (focused instanceof IContainerTooltipHandler tip) { currenttip = tip.handleItemTooltip(gui, itemstack, mousex, mousey, currenttip); + } } return currenttip; @@ -338,8 +368,11 @@ public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { - for (IContainerTooltipHandler tip : tooltipWidgets) + final Widget focused = getWidgetUnderMouse(mousex, mousey); + + if (focused instanceof IContainerTooltipHandler tip) { hotkeys = tip.handleHotkeys(gui, mousex, mousey, hotkeys); + } } return hotkeys; @@ -347,17 +380,12 @@ public Map handleHotkeys(GuiContainer gui, int mousex, int mouse public static void layout(GuiContainer gui) { VisiblityData visiblity = new VisiblityData(); - - if (isHidden()) visiblity.showNEI = false; - - if (isBookmarkPanelHidden()) visiblity.showBookmarkPanel = false; - - if (gui.height - gui.ySize <= 40 && NEIClientConfig.isSearchWidgetCentered()) - visiblity.showSearchSection = false; - - if (visiblity.showBookmarkPanel || gui.guiTop <= 20) visiblity.showSubsetDropdown = false; - - if (gui.guiLeft - 4 < 76) visiblity.showWidgets = false; + visiblity.showNEI = !isHidden(); + visiblity.showBookmarkPanel = !isBookmarkPanelHidden(); + visiblity.showSearchSection = !(gui.height - gui.ySize <= 40 && NEIClientConfig.isSearchWidgetCentered()); + visiblity.showSubsetDropdown = NEIClientConfig.showSubsetWidget() + && (gui.guiTop > 20 || visiblity.showSearchSection && !NEIClientConfig.subsetWidgetOnTop()); + visiblity.showWidgets = gui.guiLeft - 4 > 76; try { GuiInfo.readLock.lock(); @@ -467,9 +495,43 @@ public String getRenderLabel() { } }; - more = ItemPanels.itemPanel.more; - less = ItemPanels.itemPanel.less; - quantity = ItemPanels.itemPanel.quantity; + more = new Button("+") { + + @Override + public boolean onButtonPress(boolean rightclick) { + if (rightclick) return false; + + int modifier = NEIClientUtils.controlKey() ? 64 : NEIClientUtils.shiftKey() ? 10 : 1; + int quantity = NEIClientConfig.getItemQuantity() + modifier; + + if (quantity < 0) { + quantity = 0; + } + + LayoutManager.quantity.setText(Integer.toString(quantity)); + return true; + } + }; + less = new Button("-") { + + @Override + public boolean onButtonPress(boolean rightclick) { + if (rightclick) return false; + + int modifier = NEIClientUtils.controlKey() ? -64 : NEIClientUtils.shiftKey() ? -10 : -1; + int quantity = NEIClientConfig.getItemQuantity() + modifier; + + if (quantity < 0) { + quantity = 0; + } + + LayoutManager.quantity.setText(Integer.toString(quantity)); + return true; + } + }; + + quantity = new ItemQuantityField("quantity"); + delete = new Button() { @Override @@ -643,7 +705,7 @@ public void load(GuiContainer gui) { if (isEnabled()) { setInputFocused(null); - if (!itemsLoaded && isLoaded()) { + if (!itemsLoaded) { ItemList.loadItems.restart(); } @@ -677,12 +739,18 @@ public boolean checkCreativeInv(GuiContainer gui) { public static void updateWidgetVisiblities(GuiContainer gui, VisiblityData visiblity) { drawWidgets = new TreeSet<>(new WidgetZOrder(false)); controlWidgets = new TreeSet<>(new WidgetZOrder(true)); - tooltipWidgets.clear(); if (!visiblity.showNEI) return; addWidget(options); addWidget(bookmarksButton); + + if (visiblity.showItemPanel && NEIClientConfig.showItemQuantityWidget()) { + addWidget(quantity); + addWidget(more); + addWidget(less); + } + if (visiblity.showItemPanel) { addWidget(itemPanel); itemPanel.setVisible(); @@ -693,9 +761,13 @@ public static void updateWidgetVisiblities(GuiContainer gui, VisiblityData visib bookmarkPanel.setVisible(); } - searchField.setVisible(visiblity.showSearchSection); - if (visiblity.showSearchSection) { + if (visiblity.showSearchSection && NEIClientConfig.isSearchWidgetCentered() + || visiblity.showItemPanel && !NEIClientConfig.isSearchWidgetCentered()) { + searchField.setVisible(true); addWidget(searchField); + } else { + searchField.setVisible(false); + searchField.setFocus(false); } if (visiblity.showUtilityButtons) { @@ -731,10 +803,6 @@ public static LayoutStyle getLayoutStyle() { public static void addWidget(Widget widget) { drawWidgets.add(widget); controlWidgets.add(widget); - - if (widget instanceof IContainerTooltipHandler) { - tooltipWidgets.add((IContainerTooltipHandler) widget); - } } @Override @@ -750,7 +818,9 @@ public void guiTick(GuiContainer gui) { public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { if (isHidden() || !isEnabled()) return false; - for (Widget widget : drawWidgets) if (widget.onMouseWheel(scrolled, mousex, mousey)) return true; + for (Widget widget : controlWidgets) { + if (widget.onMouseWheel(scrolled, mousex, mousey)) return true; + } return false; } diff --git a/src/main/java/codechicken/nei/LayoutStyleMinecraft.java b/src/main/java/codechicken/nei/LayoutStyleMinecraft.java index 22e5c5741..c15bedc8b 100644 --- a/src/main/java/codechicken/nei/LayoutStyleMinecraft.java +++ b/src/main/java/codechicken/nei/LayoutStyleMinecraft.java @@ -1,34 +1,20 @@ package codechicken.nei; import static codechicken.lib.gui.GuiDraw.drawStringC; -import static codechicken.nei.LayoutManager.bookmarkPanel; -import static codechicken.nei.LayoutManager.bookmarksButton; -import static codechicken.nei.LayoutManager.delete; -import static codechicken.nei.LayoutManager.dropDown; -import static codechicken.nei.LayoutManager.gamemode; -import static codechicken.nei.LayoutManager.heal; -import static codechicken.nei.LayoutManager.itemPanel; -import static codechicken.nei.LayoutManager.itemPresenceOverlays; -import static codechicken.nei.LayoutManager.itemZoom; -import static codechicken.nei.LayoutManager.magnet; -import static codechicken.nei.LayoutManager.options; -import static codechicken.nei.LayoutManager.rain; -import static codechicken.nei.LayoutManager.searchField; -import static codechicken.nei.LayoutManager.timeButtons; -import static codechicken.nei.NEIClientConfig.canPerformAction; -import static codechicken.nei.NEIClientConfig.disabledActions; -import static codechicken.nei.NEIClientConfig.getMagnetMode; -import static codechicken.nei.NEIClientConfig.isEnabled; import net.minecraft.client.gui.inventory.GuiContainer; import org.lwjgl.opengl.GL11; +import codechicken.lib.vec.Rectangle4i; import codechicken.nei.api.LayoutStyle; import codechicken.nei.drawable.DrawableBuilder; public class LayoutStyleMinecraft extends LayoutStyle { + protected static final int BUTTON_SIZE = 20; + protected static final int MARGIN = 2; + public int buttonCount; public int leftSize; public int numButtons; @@ -40,19 +26,21 @@ public String getName() { @Override public void init() { - delete.icon = new Image(144, 12, 12, 12); - rain.icon = new Image(120, 12, 12, 12); - gamemode.icons[0] = new Image(132, 12, 12, 12); - gamemode.icons[1] = new Image(156, 12, 12, 12); - gamemode.icons[2] = new Image(168, 12, 12, 12); - magnet.icon = new Image(180, 24, 12, 12); - timeButtons[0].icon = new Image(132, 24, 12, 12); - timeButtons[1].icon = new Image(120, 24, 12, 12); - timeButtons[2].icon = new Image(144, 24, 12, 12); - timeButtons[3].icon = new Image(156, 24, 12, 12); - heal.icon = new Image(168, 24, 12, 12); - itemPresenceOverlays[0] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 0, 40, 8, 8).build(); - itemPresenceOverlays[1] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 8, 40, 8, 8).build(); + LayoutManager.delete.icon = new Image(144, 12, 12, 12); + LayoutManager.rain.icon = new Image(120, 12, 12, 12); + LayoutManager.gamemode.icons[0] = new Image(132, 12, 12, 12); + LayoutManager.gamemode.icons[1] = new Image(156, 12, 12, 12); + LayoutManager.gamemode.icons[2] = new Image(168, 12, 12, 12); + LayoutManager.magnet.icon = new Image(180, 24, 12, 12); + LayoutManager.timeButtons[0].icon = new Image(132, 24, 12, 12); + LayoutManager.timeButtons[1].icon = new Image(120, 24, 12, 12); + LayoutManager.timeButtons[2].icon = new Image(144, 24, 12, 12); + LayoutManager.timeButtons[3].icon = new Image(156, 24, 12, 12); + LayoutManager.heal.icon = new Image(168, 24, 12, 12); + LayoutManager.itemPresenceOverlays[0] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 0, 40, 8, 8) + .build(); + LayoutManager.itemPresenceOverlays[1] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 8, 40, 8, 8) + .build(); } @Override @@ -65,85 +53,177 @@ public void layout(GuiContainer gui, VisiblityData visiblity) { reset(); leftSize = ItemPanels.bookmarkPanel.getWidth(gui); - numButtons = Math.max(leftSize / 18, 1); + numButtons = Math.max(leftSize / 20, 1); - delete.state = 0x4; - if (NEIController.getDeleteMode()) delete.state |= 1; - else if (!visiblity.enableDeleteMode) delete.state |= 2; + LayoutManager.delete.state = 0x4; + if (NEIController.getDeleteMode()) { + LayoutManager.delete.state |= 1; + } else if (!visiblity.enableDeleteMode) { + LayoutManager.delete.state |= 2; + } - rain.state = 0x4; - if (disabledActions.contains("rain")) rain.state |= 2; - else if (NEIClientUtils.isRaining()) rain.state |= 1; + LayoutManager.rain.state = 0x4; + if (NEIClientConfig.disabledActions.contains("rain")) { + LayoutManager.rain.state |= 2; + } else if (NEIClientUtils.isRaining()) { + LayoutManager.rain.state |= 1; + } - gamemode.state = 0x4; + LayoutManager.gamemode.state = 0x4; if (NEIClientUtils.getGamemode() != 0) { - gamemode.state |= 0x1; - gamemode.index = NEIClientUtils.getGamemode() - 1; + LayoutManager.gamemode.state |= 0x1; + LayoutManager.gamemode.index = NEIClientUtils.getGamemode() - 1; } else { - if (NEIClientUtils.isValidGamemode("creative")) gamemode.index = 0; - else if (NEIClientUtils.isValidGamemode("creative+")) gamemode.index = 1; - else if (NEIClientUtils.isValidGamemode("adventure")) gamemode.index = 2; + if (NEIClientUtils.isValidGamemode("creative")) { + LayoutManager.gamemode.index = 0; + } else if (NEIClientUtils.isValidGamemode("creative+")) { + LayoutManager.gamemode.index = 1; + } else if (NEIClientUtils.isValidGamemode("adventure")) { + LayoutManager.gamemode.index = 2; + } } - bookmarksButton.index = NEIClientConfig.isBookmarkPanelHidden() ? 0 : 1; - options.index = NEIClientConfig.getCheatMode(); + LayoutManager.bookmarksButton.index = NEIClientConfig.isBookmarkPanelHidden() ? 0 : 1; + LayoutManager.options.index = NEIClientConfig.getCheatMode(); + + LayoutManager.magnet.state = 0x4 | (NEIClientConfig.getMagnetMode() ? 1 : 0); - magnet.state = 0x4 | (getMagnetMode() ? 1 : 0); + if (NEIClientConfig.canPerformAction("delete")) { + layoutButton(LayoutManager.delete); + } + + if (NEIClientConfig.canPerformAction("rain")) { + layoutButton(LayoutManager.rain); + } - if (canPerformAction("delete")) layoutButton(delete); - if (canPerformAction("rain")) layoutButton(rain); if (NEIClientUtils.isValidGamemode("creative") || NEIClientUtils.isValidGamemode("creative+") - || NEIClientUtils.isValidGamemode("adventure")) - layoutButton(gamemode); - if (canPerformAction("magnet")) layoutButton(magnet); - if (canPerformAction("time")) { + || NEIClientUtils.isValidGamemode("adventure")) { + layoutButton(LayoutManager.gamemode); + } + + if (NEIClientConfig.canPerformAction("magnet")) { + layoutButton(LayoutManager.magnet); + } + + if (NEIClientConfig.canPerformAction("time")) { for (int i = 0; i < 4; i++) { - timeButtons[i].state = disabledActions.contains(NEIActions.timeZones[i]) ? 2 : 0; - layoutButton(timeButtons[i]); + LayoutManager.timeButtons[i].state = NEIClientConfig.disabledActions.contains(NEIActions.timeZones[i]) + ? 2 + : 0; + layoutButton(LayoutManager.timeButtons[i]); } } - if (canPerformAction("heal")) layoutButton(heal); - itemPanel.resize(gui); - bookmarkPanel.resize(gui); - itemZoom.resize(gui); + if (NEIClientConfig.canPerformAction("heal")) { + layoutButton(LayoutManager.heal); + } + + layoutFooter(gui, visiblity); + + LayoutManager.itemPanel.resize(gui); + LayoutManager.bookmarkPanel.resize(gui); + LayoutManager.itemZoom.resize(gui); + } + + protected void layoutFooter(GuiContainer gui, VisiblityData visiblity) { - options.x = isEnabled() ? 0 : 6; - options.y = isEnabled() ? gui.height - 22 : gui.height - 28; - options.w = 22; - options.h = 22; + LayoutManager.options.x = MARGIN; + LayoutManager.options.y = gui.height - BUTTON_SIZE - MARGIN; + LayoutManager.options.w = LayoutManager.options.h = BUTTON_SIZE; - bookmarksButton.x = 24 + (isEnabled() ? 0 : 6); - bookmarksButton.y = isEnabled() ? gui.height - 22 : gui.height - 28; - bookmarksButton.w = 22; - bookmarksButton.h = 22; + LayoutManager.bookmarksButton.x = LayoutManager.options.x + LayoutManager.options.w + MARGIN; + LayoutManager.bookmarksButton.y = gui.height - BUTTON_SIZE - MARGIN; + LayoutManager.bookmarksButton.w = LayoutManager.bookmarksButton.h = BUTTON_SIZE; - dropDown.h = 18; - dropDown.w = 150; - dropDown.y = 0; - dropDown.x = (gui.width - gui.xSize) / 2 + gui.xSize - dropDown.w; + final Rectangle4i searchArea = getSearchFieldArea(gui); + final Rectangle4i quantityArea = getQuantityFieldArea(visiblity); + + LayoutManager.searchField.x = searchArea.x; + LayoutManager.searchField.y = searchArea.y; + LayoutManager.searchField.w = searchArea.w; + LayoutManager.searchField.h = searchArea.h; + + if (!NEIClientConfig.subsetWidgetOnTop() && visiblity.showSubsetDropdown) { + LayoutManager.dropDown.x = searchArea.x; + LayoutManager.dropDown.y = searchArea.y; + LayoutManager.dropDown.h = LayoutManager.dropDown.w = BUTTON_SIZE; + + LayoutManager.searchField.x += BUTTON_SIZE; + LayoutManager.searchField.w -= BUTTON_SIZE; + } else { + LayoutManager.dropDown.h = 16; + LayoutManager.dropDown.w = 150; + LayoutManager.dropDown.y = MARGIN; + LayoutManager.dropDown.x = (gui.width - LayoutManager.dropDown.w) / 2; + } + + if (quantityArea.w == ItemPanels.itemPanel.w) { + LayoutManager.more.w = LayoutManager.less.w = BUTTON_SIZE; + LayoutManager.more.h = LayoutManager.less.h = LayoutManager.quantity.h = quantityArea.h; + LayoutManager.more.y = LayoutManager.less.y = LayoutManager.quantity.y = quantityArea.y; + + LayoutManager.less.x = quantityArea.x; + LayoutManager.more.x = quantityArea.x2() - BUTTON_SIZE; + + LayoutManager.quantity.x = LayoutManager.less.x + LayoutManager.less.w + 1; + LayoutManager.quantity.w = LayoutManager.more.x - LayoutManager.quantity.x - 1; + } else { + LayoutManager.quantity.x = quantityArea.x; + LayoutManager.quantity.w = quantityArea.w - 18; + LayoutManager.quantity.y = LayoutManager.more.y = quantityArea.y; + LayoutManager.quantity.h = quantityArea.h; + + LayoutManager.more.w = LayoutManager.less.w = 18; + LayoutManager.more.x = LayoutManager.less.x = LayoutManager.quantity.x + LayoutManager.quantity.w; + LayoutManager.more.h = LayoutManager.less.h = LayoutManager.quantity.h / 2; + LayoutManager.less.y = LayoutManager.more.y + LayoutManager.more.h; + } + + } + + private Rectangle4i getSearchFieldArea(GuiContainer gui) { + final Rectangle4i area = new Rectangle4i(0, 0, 0, BUTTON_SIZE); - searchField.h = 20; if (NEIClientConfig.isSearchWidgetCentered()) { - searchField.w = 150; - searchField.x = (gui.width - searchField.w) / 2; + area.w = gui.xSize - MARGIN * 2; + area.x = (gui.width - area.w) / 2; + area.y = gui.height - BUTTON_SIZE - MARGIN; + } else if (NEIClientConfig.showItemQuantityWidget()) { + area.w = ItemPanels.itemPanel.w - (int) Math.max(BUTTON_SIZE * 2.5f, ItemPanels.itemPanel.w * 0.3f) - 1; + area.x = ItemPanels.itemPanel.x; + area.y = ItemPanels.itemPanel.y + ItemPanels.itemPanel.h - BUTTON_SIZE; } else { - searchField.w = (int) (itemPanel.w * (NEIClientConfig.showItemQuantityWidget() ? 0.7 : 1)); - searchField.x = itemPanel.x; + area.w = ItemPanels.itemPanel.w; + area.x = ItemPanels.itemPanel.x; + area.y = ItemPanels.itemPanel.y + ItemPanels.itemPanel.h - BUTTON_SIZE; } - searchField.y = gui.height - searchField.h - 2; - if (!visiblity.showItemSection) { - searchField.setFocus(false); + return area; + } + + private Rectangle4i getQuantityFieldArea(VisiblityData visiblity) { + final Rectangle4i area = new Rectangle4i(0, 0, 0, BUTTON_SIZE); + + if (!visiblity.showSearchSection || NEIClientConfig.isSearchWidgetCentered()) { + area.w = ItemPanels.itemPanel.w; + area.x = ItemPanels.itemPanel.x; + } else { + area.w = (int) Math.max(BUTTON_SIZE * 2.5f, ItemPanels.itemPanel.w * 0.3f); + area.x = ItemPanels.itemPanel.x + ItemPanels.itemPanel.w - area.w; } + + area.y = ItemPanels.itemPanel.y + ItemPanels.itemPanel.h - BUTTON_SIZE; + + return area; } public void layoutButton(Button button) { - button.x = 2 + (buttonCount % numButtons) * 19; - button.y = 2 + (buttonCount / numButtons) * 18; button.h = 17; button.w = button.contentWidth() + 6; + button.x = MARGIN + (buttonCount % numButtons) * (button.w + MARGIN); + button.y = MARGIN + (buttonCount / numButtons) * (button.h + MARGIN); + buttonCount++; } @@ -177,9 +257,16 @@ else if ((b.state & 0x4) == 0 && b.contains(mousex, mousey) || // not a state bu @Override public void drawSubsetTag(String text, int x, int y, int w, int h, int state, boolean mouseover) { - if (state == 1) GL11.glColor4f(0.65F, 0.65F, 0.65F, 1.0F); - else GL11.glColor4f(1, 1, 1, 1); + if (state == 1) { + GL11.glColor4f(0.65F, 0.65F, 0.65F, 1.0F); + } else { + GL11.glColor4f(1, 1, 1, 1); + } + LayoutManager.drawButtonBackground(x, y, w, h, false, state == 0 ? 0 : 1); - if (text != null) drawStringC(text, x, y, w, h, state == 2 ? 0xFFE0E0E0 : 0xFFA0A0A0); + + if (text != null) { + drawStringC(text, x, y, w, h, mouseover ? 0xFFFFFFA0 : (state == 2 ? 0xFFE0E0E0 : 0xFFA0A0A0)); + } } } diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index 00126d4ef..9fb05729c 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -233,6 +233,12 @@ public boolean onClick(int button) { .setComment("Require holding shift to move items when overlaying recipe").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.guirecipe.shiftOverlayRecipe", true)); + tag.getTag("inventory.subsets.enabled").setComment("Enable/disable Subsets Dropdown").getBooleanValue(true); + API.addOption(new OptionToggleButton("inventory.subsets.enabled", true)); + + tag.getTag("inventory.subsets.widgetPosition").setComment("Subsets Widget Position").getBooleanValue(true); + API.addOption(new OptionToggleButton("inventory.subsets.widgetPosition", true)); + tag.getTag("inventory.guirecipe.profile").getBooleanValue(false); API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true)); @@ -709,8 +715,6 @@ public static void loadWorld(String worldPath) { world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg"))); bootNEI(ClientUtils.getWorld()); - CollapsibleItems.load(); - ItemPanels.bookmarkPanel.load(); onWorldLoad(newWorld); } @@ -725,11 +729,12 @@ private static void onWorldLoad(boolean newWorld) { setWorldDefaults(); creativeInv = new ItemStack[54]; LayoutManager.searchField.setText(getSearchExpression()); - ItemPanels.itemPanel.quantity.setText(Integer.toString(getItemQuantity())); - SubsetWidget.loadHidden(); + LayoutManager.quantity.setText(Integer.toString(getItemQuantity())); - if (newWorld && Minecraft.getMinecraft().isSingleplayer()) world.config.getTag("inventory.cheatmode") - .setIntValue(NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0); + if (newWorld && Minecraft.getMinecraft().isSingleplayer()) { + world.config.getTag("inventory.cheatmode") + .setIntValue(NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0); + } NEIInfo.load(ClientUtils.getWorld()); } @@ -748,6 +753,7 @@ public static void unloadWorld() { ItemPanels.bookmarkPanel.saveBookmarks(); } + SubsetWidget.saveHidden(); CollapsibleItems.saveStates(); if (world != null) { @@ -810,6 +816,9 @@ public void run() { }); RecipeCatalysts.loadCatalystInfo(); + ItemPanels.bookmarkPanel.load(); + SubsetWidget.loadHidden(); + CollapsibleItems.load(); ItemSorter.loadConfig(); // Set pluginNEIConfigLoaded here before posting the NEIConfigsLoadedEvent. This used to be the @@ -824,6 +833,8 @@ public void run() { } }.start(); } else { + ItemPanels.bookmarkPanel.load(); + SubsetWidget.loadHidden(); ItemList.loadItems.restart(); } } @@ -870,6 +881,14 @@ public static boolean isSearchWidgetCentered() { return getBooleanSetting("inventory.search.widgetPosition"); } + public static boolean showSubsetWidget() { + return getBooleanSetting("inventory.subsets.enabled"); + } + + public static boolean subsetWidgetOnTop() { + return getBooleanSetting("inventory.subsets.widgetPosition"); + } + public static int searchWidgetAutofocus() { return getIntSetting("inventory.search.widgetAutofocus"); } diff --git a/src/main/java/codechicken/nei/SubsetWidget.java b/src/main/java/codechicken/nei/SubsetWidget.java index 79b0f5941..79a3bf643 100644 --- a/src/main/java/codechicken/nei/SubsetWidget.java +++ b/src/main/java/codechicken/nei/SubsetWidget.java @@ -1,22 +1,19 @@ package codechicken.nei; -import static codechicken.nei.NEIClientUtils.translate; - import java.awt.Point; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.TreeMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.regex.Pattern; +import java.util.stream.Collectors; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; @@ -28,7 +25,6 @@ import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.ItemList.AnyMultiItemFilter; -import codechicken.nei.ItemList.ItemsLoadedCallback; import codechicken.nei.ItemList.NothingItemFilter; import codechicken.nei.SearchTokenParser.ISearchParserProvider; import codechicken.nei.SearchTokenParser.SearchMode; @@ -36,18 +32,17 @@ import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemFilter.ItemFilterProvider; import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.guihook.IContainerTooltipHandler; -public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoadedCallback { - - public static ReentrantReadWriteLock hiddenItemLock = new ReentrantReadWriteLock(); - public static Lock writeLock = hiddenItemLock.writeLock(); - public static Lock readLock = hiddenItemLock.readLock(); +public class SubsetWidget extends Button implements ItemFilterProvider, IContainerTooltipHandler { - public static class SubsetState { + protected static final int SLOT_HEIGHT = 18; + protected static final int MARGIN = 2; + protected static final char PREFIX = '%'; - int state = 2; - ArrayList items = new ArrayList<>(); - } + protected static ReentrantReadWriteLock hiddenItemLock = new ReentrantReadWriteLock(); + protected static Lock hiddenWriteLock = hiddenItemLock.writeLock(); + protected static Lock hiddenReadLock = hiddenItemLock.readLock(); public static class SubsetTag { @@ -60,60 +55,72 @@ public SubsetSlot() { @Override public int getSlotHeight(int slot) { - return 18; + return SubsetWidget.SLOT_HEIGHT; } @Override protected int getNumSlots() { - return children.size() + state.items.size(); + return children.size() + items.size(); } @Override protected void slotClicked(int slot, int button, int mx, int my, int count) { - if (slot < sorted.size()) { - SubsetTag tag = sorted.get(slot); - if (NEIClientUtils.shiftKey()) { - LayoutManager.searchField.setText( - SearchField.searchParser.getRedefinedPrefix('%') + tag.fullname.replaceAll("\\s+", "")); + if (slot < children.size()) { + SubsetTag tag = children.get(slot); + + if (SubsetWidget.enableSearchBySubsets && NEIClientUtils.shiftKey()) { + LayoutManager.searchField + .setText(SearchField.searchParser.getRedefinedPrefix(SubsetWidget.PREFIX) + tag.path); } else if (button == 0 && count >= 2) { SubsetWidget.showOnly(tag); - } else { + } else if (button == 0 || button == 1) { SubsetWidget.setHidden(tag, button == 1); } + } else { - ItemStack item = state.items.get(slot - sorted.size()); - if (NEIClientUtils.controlKey()) { + ItemStack item = items.get(slot - children.size()); + + if (NEIClientUtils.controlKey() && NEIClientConfig.canCheatItem(item)) { NEIClientUtils.cheatItem(item, button, -1); - } else { - SubsetWidget.setHidden(state.items.get(slot - sorted.size()), button == 1); + } else if (SubsetWidget.enableSearchBySubsets && NEIClientUtils.shiftKey()) { + LayoutManager.searchField.setText(SearchField.getEscapedSearchText(item)); + } else if (button == 0 || button == 1) { + SubsetWidget.setHidden(item, button == 1); } + } } @Override protected void drawSlot(int slot, int x, int y, int mx, int my, float frame) { int w = windowBounds().width; - Rectangle4i r = new Rectangle4i(x, y, w, getSlotHeight(slot)); - if (slot < sorted.size()) { - SubsetTag tag = sorted.get(slot); + Rectangle4i r = new Rectangle4i(0, 0, w, getSlotHeight(slot)); + + if (slot < children.size()) { + SubsetTag tag = children.get(slot); LayoutManager.getLayoutStyle() - .drawSubsetTag(tag.displayName(), x, y, r.w, r.h, tag.state.state, r.contains(mx, my)); + .drawSubsetTag(tag.displayName(), x, y, r.w, r.h, tag.state, r.contains(mx, my)); + + if (r.contains(mx, my)) { + SubsetWidget.hoverTag = tag; + } } else { - ItemStack stack = state.items.get(slot - sorted.size()); + ItemStack stack = items.get(slot - children.size()); boolean hidden = SubsetWidget.isHidden(stack); - int itemx = w / 2 - 8; - int itemy = 1; - - LayoutManager.getLayoutStyle().drawSubsetTag(null, x, y, r.w, r.h, hidden ? 0 : 2, false); + LayoutManager.getLayoutStyle() + .drawSubsetTag(null, x, y, r.w, r.h, hidden ? 0 : 2, r.contains(mx, my)); + GuiContainerManager.drawItem(x + (w / 2 - 8), y + 1, stack); - GuiContainerManager.drawItem(x + itemx, y + itemy, stack); - if (new Rectangle4i(itemx, itemy, 16, 16).contains(mx, my)) SubsetWidget.hoverStack = stack; + if (r.contains(mx, my)) { + SubsetWidget.hoverStack = stack; + } } } @Override - public void drawOverlay(float frame) {} + public void drawOverlay(float frame) { /** disabled draw overlay */ + } @Override public void drawBackground(float frame) { @@ -122,7 +129,7 @@ public void drawBackground(float frame) { @Override public int scrollbarAlignment() { - return -1; + return marginleft == 0 ? 1 : -1; } @Override @@ -137,111 +144,82 @@ public int scrollbarGuideAlignment() { } public final String fullname; + public final String path; + public final String parentPath; public final ItemFilter filter; - public TreeMap children = new TreeMap<>(); - public List sorted = Collections.emptyList(); - private int childwidth; - protected SubsetState state = new SubsetState(); + // 0 - full hidden + // 1 - partitial hidden + // 2 - enabled + public int state = 2; + public int calculatedWidth; + public final List items; + public final List children = new ArrayList<>(); protected final SubsetSlot slot = new SubsetSlot(); + private SubsetTag selectedChild; private int visible; public SubsetTag(String fullname) { - this(fullname, new NothingItemFilter()); + this(fullname, null); } public SubsetTag(String fullname, ItemFilter filter) { - assert filter != null : "Filter cannot be null"; this.fullname = EnumChatFormatting.getTextWithoutFormattingCodes(fullname); - this.filter = filter; - } - - public String displayName() { - String translated = translate("subsets." + fullname); - if (!translated.startsWith("nei.")) return translated; - - int idx = fullname.lastIndexOf('.'); - return idx < 0 ? fullname : fullname.substring(idx + 1); - } - - public String name() { - int idx = fullname.indexOf('.'); - return idx < 0 ? fullname : fullname.substring(idx + 1); - } - - public String parent() { - int idx = fullname.lastIndexOf('.'); - return idx < 0 ? null : fullname.substring(0, idx); - } - - private SubsetTag getTag(String name) { - int idx = name.indexOf('.'); - String childname = idx > 0 ? name.substring(0, idx) : name; - SubsetTag child = children.get(childname.replaceAll("\\s+", "").toLowerCase()); - if (child == null) return null; - - return idx > 0 ? child.getTag(name.substring(idx + 1)) : child; - } - - private void recacheChildren() { - sorted = new ArrayList<>(children.values()); - childwidth = 0; - for (SubsetTag tag : sorted) childwidth = Math.max(childwidth, tag.nameWidth() + 2); - } + this.filter = filter == null ? new NothingItemFilter() : filter; + this.items = new ArrayList<>(); - private void addTag(SubsetTag tag) { - String name = fullname == null ? tag.fullname : tag.fullname.substring(fullname.length() + 1); - int idx = name.indexOf('.'); + if (this.fullname != null) { + this.path = this.fullname.replaceAll("\\s+", "").toLowerCase(); - if (idx < 0) { // add or replace tag - SubsetTag prev = children.put(name.replaceAll("\\s+", "").toLowerCase(), tag); - if (prev != null) { // replaced, load children - tag.children = prev.children; - tag.sorted = prev.sorted; - } - recacheChildren(); + int idx = this.path.lastIndexOf('.'); + this.parentPath = idx < 0 ? null : this.path.substring(0, idx); } else { - String childname = name.substring(0, idx); - SubsetTag child = children.get(childname.replaceAll("\\s+", "").toLowerCase()); - if (child == null) { - children.put( - childname.replaceAll("\\s+", "").toLowerCase(), - child = new SubsetTag(fullname == null ? childname : fullname + '.' + childname)); - } - recacheChildren(); - child.addTag(tag); + this.parentPath = this.path = null; } } - protected void cacheState() { - state = SubsetWidget.getState(this); - for (SubsetTag tag : sorted) tag.cacheState(); + public String displayName() { + final String translated = NEIClientUtils.translate("subsets." + this.fullname); + return translated.startsWith("nei.") ? name() : translated; } - public void addFilters(List filters) { - if (filter != null) filters.add(filter); + public String name() { + int idx = this.fullname.indexOf('.'); + return idx < 0 ? this.fullname : this.fullname.substring(idx + 1); + } - for (SubsetTag child : sorted) child.addFilters(filters); + public String parent() { + int idx = this.fullname.lastIndexOf('.'); + return idx < 0 ? null : this.fullname.substring(0, idx); } - public void search(List tags, Pattern p) { - if (fullname != null && p.matcher(fullname.toLowerCase()).find()) tags.add(this); - else for (SubsetTag child : sorted) child.search(tags, p); + public void clearCache() { + this.state = 2; + this.items.clear(); + this.children.clear(); + this.calculatedWidth = 0; } public void updateVisiblity(int mx, int my) { if (selectedChild != null) { selectedChild.updateVisiblity(mx, my); - if (!selectedChild.isVisible()) selectedChild = null; + if (!selectedChild.isVisible()) { + selectedChild = null; + } } if (slot.contains(mx, my) && (selectedChild == null || !selectedChild.contains(mx, my))) { int mslot = slot.getClickedSlot(my); - if (mslot >= 0 && mslot < sorted.size()) { - SubsetTag mtag = sorted.get(mslot); + + if (mslot >= 0 && mslot < children.size()) { + SubsetTag mtag = children.get(mslot); if (mtag != null) { - if (mtag != selectedChild && selectedChild != null) selectedChild.setHidden(); + + if (mtag != selectedChild && selectedChild != null) { + selectedChild.setHidden(); + } + selectedChild = mtag; selectedChild.setVisible(); } @@ -250,65 +228,76 @@ public void updateVisiblity(int mx, int my) { setVisible(); } - if (selectedChild == null) countdownVisible(); + if (this.selectedChild == null) { + countdownVisible(); + } } public void setHidden() { - visible = 0; + this.visible = 0; slot.mouseMovedOrUp(0, 0, 0); // cancel any scrolling - if (selectedChild != null) { - selectedChild.setHidden(); - selectedChild = null; + if (this.selectedChild != null) { + this.selectedChild.setHidden(); + this.selectedChild = null; } } public void setVisible() { - visible = 10; - cacheState(); + this.visible = 10; } private void countdownVisible() { - if (visible > 0 && --visible == 0) setHidden(); + if (this.visible > 0 && --this.visible == 0) { + setHidden(); + } } - public void resize(int x, int pwidth, int y) { - int mheight = area.h; - int dheight = area.y2() - y; - int cheight = slot.contentHeight(); - int height = cheight; - if (cheight > mheight) { - y = area.y; - height = mheight; - } else if (cheight > dheight) { - y = area.y2() - cheight; + public void resize(Rectangle4i screen, Rectangle4i parent, boolean dropRight) { + int height = Math.min(slot.contentHeight(), screen.h); + int width = Math.max(calculatedWidth + 2, this.items.isEmpty() ? 0 : 16 + MARGIN * 2); + int scrollbarWidth = slot.scrollbarDim().width; + + if (slot.contentHeight() > height) { + width += scrollbarWidth; } - height = height / slot.getSlotHeight(0) * slot.getSlotHeight(0); // floor to a multiple of slot height + int slotY = parent.y1() + Math.min(0, alignValueToStep(screen.y2() - parent.y1() - height)); + int slotX = dropRight ? parent.x2() : parent.x1() - width; - int width = childwidth; - if (!state.items.isEmpty()) width = Math.max(childwidth, 18); - if (slot.contentHeight() > height) width += slot.scrollbarDim().width; + if (slotX + width >= screen.x2()) { + slotX = parent.x1() - width; + dropRight = false; + } else if (slotX <= screen.x1()) { + slotX = parent.x2(); + dropRight = true; + } - boolean fitLeft = x - width >= area.x1(); - boolean fitRight = x + width + pwidth <= area.x2(); - if (pwidth >= 0 ? !fitRight && fitLeft : !fitLeft) pwidth *= -1; // swap - x += pwidth >= 0 ? pwidth : -width; + slot.setSize(slotX, slotY, width, height); - slot.setSize(x, y, width, height); - slot.setMargins(slot.hasScrollbar() ? slot.scrollbarDim().width : 0, 0, 0, 0); + if (dropRight) { + slot.setMargins(0, 0, slot.hasScrollbar() ? scrollbarWidth : 0, 0); + } else { + slot.setMargins(slot.hasScrollbar() ? scrollbarWidth : 0, 0, 0, 0); + } if (selectedChild != null) { - y = slot.getSlotY(sorted.indexOf(selectedChild)) - slot.scrolledPixels() + slot.y; - selectedChild.resize(x, pwidth >= 0 ? width : -width, y); + selectedChild.resize( + screen, + new Rectangle4i( + slot.x, + slot.y + slot.getSlotY(this.children.indexOf(selectedChild)) - slot.scrolledPixels(), + width, + SLOT_HEIGHT), + dropRight); } } protected int nameWidth() { - return Minecraft.getMinecraft().fontRenderer.getStringWidth(displayName()); + return Minecraft.getMinecraft().fontRenderer.getStringWidth(displayName()) + 4; } public boolean isVisible() { - return visible > 0; + return this.visible > 0; } public void draw(int mx, int my) { @@ -321,18 +310,25 @@ public boolean contains(int px, int py) { } public void mouseClicked(int mx, int my, int button) { - if (selectedChild != null && selectedChild.contains(mx, my)) selectedChild.mouseClicked(mx, my, button); - else if (slot.contains(mx, my)) slot.mouseClicked(mx, my, button); + if (selectedChild != null && selectedChild.contains(mx, my)) { + selectedChild.mouseClicked(mx, my, button); + } else if (slot.contains(mx, my)) { + slot.mouseClicked(mx, my, button); + } } public void mouseDragged(int mx, int my, int button, long heldTime) { slot.mouseDragged(mx, my, button, heldTime); - if (selectedChild != null) selectedChild.mouseDragged(mx, my, button, heldTime); + if (selectedChild != null) { + selectedChild.mouseDragged(mx, my, button, heldTime); + } } public void mouseUp(int mx, int my, int button) { slot.mouseMovedOrUp(mx, my, button); - if (selectedChild != null) selectedChild.mouseUp(mx, my, button); + if (selectedChild != null) { + selectedChild.mouseUp(mx, my, button); + } } public boolean mouseScrolled(int mx, int my, int scroll) { @@ -360,21 +356,11 @@ private static class DefaultParserProvider implements ISearchParserProvider { public ItemFilter getFilter(String searchText) { final AnyMultiItemFilter filter = new AnyMultiItemFilter(); - final SubsetTag tag = getTag(searchText); + final String pathPart = searchText.replaceAll("\\s+", "").toLowerCase(); - if (tag != null) { - // We've got an exact match - tag.addFilters(filter.filters); - } else { - // Try searching for a substring - Pattern p = SearchField.getPattern(searchText); - if (p == null) return null; - - List matching = new LinkedList<>(); - root.search(matching, p); - if (matching.isEmpty()) return null; - for (SubsetTag tag2 : matching) { - tag2.addFilters(filter.filters); + for (SubsetTag tag : tags.values()) { + if (tag.filter != null && tag.path.contains(pathPart)) { + filter.filters.add(tag.filter); } } @@ -382,7 +368,7 @@ public ItemFilter getFilter(String searchText) { } public char getPrefix() { - return '%'; + return SubsetWidget.PREFIX; } public EnumChatFormatting getHighlightedColor() { @@ -395,42 +381,42 @@ public SearchMode getSearchMode() { } } - protected static final SubsetTag root = new SubsetTag(null); - public static Rectangle4i area = new Rectangle4i(); - public static ItemStack hoverStack; - - private static HashMap subsetState = new HashMap<>(); /** * All operations on this variable should be synchronised. */ private static final ItemStackSet hiddenItems = new ItemStackSet(); - - private static final AtomicReference dirtyHiddenItems = new AtomicReference<>(); - - public static SubsetState getState(SubsetTag tag) { - SubsetState state = subsetState.get(tag.fullname); - return state == null ? new SubsetState() : state; - } + private static final Map tags = new HashMap<>(); + private static final SubsetTag root = new SubsetTag(""); + protected static boolean enableSearchBySubsets = false; + protected static ItemStack hoverStack; + protected static SubsetTag hoverTag; + private long lastclicktime; public static void addTag(SubsetTag tag) { updateState.stop(); - synchronized (root) { - root.addTag(tag); - updateState.reallocate(); - } - } - public static SubsetTag getTag(String name) { - return name == null ? root : root.getTag(name); + synchronized (tags) { + tags.put(tag.path, tag); + + String parentname = tag.parent(); + + while (parentname != null && !tags.containsKey(parentname.replaceAll("\\s+", "").toLowerCase())) { + SubsetTag parent = new SubsetTag(parentname); + tags.put(parent.path, parent); + parentname = parent.parent(); + } + + updateHiddenItems(); + } } public static boolean isHidden(ItemStack item) { try { - if (readLock.tryLock(5, TimeUnit.SECONDS)) { + if (hiddenReadLock.tryLock(5, TimeUnit.SECONDS)) { try { return hiddenItems.contains(item); } finally { - readLock.unlock(); + hiddenReadLock.unlock(); } } else { NEIClientConfig.logger.error("Unable to obtain read lock in 'isHidden'"); @@ -441,30 +427,31 @@ public static boolean isHidden(ItemStack item) { return false; } - private static void _setHidden(SubsetTag tag, boolean hidden) { - for (ItemStack item : getState(tag).items) _setHidden(item, hidden); - for (SubsetTag child : tag.sorted) _setHidden(child, hidden); - } + private static List getItems(SubsetTag tag, List items) { + items.addAll(tag.items); - private static void _setHidden(ItemStack item, boolean hidden) { - if (hiddenItemLock.isWriteLockedByCurrentThread()) { - if (hidden) hiddenItems.add(item); - else hiddenItems.remove(item); + for (SubsetTag child : tag.children) { + getItems(child, items); } + + return items; } public static void showOnly(SubsetTag tag) { try { - if (writeLock.tryLock(5, TimeUnit.SECONDS)) { + if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) { try { - for (ItemStack item : ItemList.items) _setHidden(item, true); - setHidden(tag, false); + hiddenItems.clear(); + hiddenItems.addAll(getItems(root, new ArrayList<>())); + hiddenItems.removeAll(getItems(tag, new ArrayList<>())); } finally { - writeLock.unlock(); + hiddenWriteLock.unlock(); } } else { NEIClientConfig.logger.error("Unable to obtain write lock in 'showOnly'"); } + + calculateVisibility(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -472,16 +459,24 @@ public static void showOnly(SubsetTag tag) { public static void setHidden(SubsetTag tag, boolean hidden) { try { - if (writeLock.tryLock(5, TimeUnit.SECONDS)) { + if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) { try { - _setHidden(tag, hidden); - updateHiddenItems(); + List tagItems = getItems(tag, new ArrayList<>()); + + if (hidden) { + hiddenItems.addAll(tagItems); + } else { + hiddenItems.removeAll(tagItems); + } + } finally { - writeLock.unlock(); + hiddenWriteLock.unlock(); } } else { NEIClientConfig.logger.error("Unable to obtain write lock in 'setHidden'"); } + + calculateVisibility(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -489,16 +484,23 @@ public static void setHidden(SubsetTag tag, boolean hidden) { public static void setHidden(ItemStack item, boolean hidden) { try { - if (writeLock.tryLock(5, TimeUnit.SECONDS)) { + if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) { try { - _setHidden(item, hidden); - updateHiddenItems(); + + if (hidden) { + hiddenItems.add(item); + } else { + hiddenItems.remove(item); + } + } finally { - writeLock.unlock(); + hiddenWriteLock.unlock(); } } else { NEIClientConfig.logger.error("Unable to obtain write lock in 'setHidden'"); } + + calculateVisibility(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -506,58 +508,49 @@ public static void setHidden(ItemStack item, boolean hidden) { public static void unhideAll() { try { - if (writeLock.tryLock(5, TimeUnit.SECONDS)) { + if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) { try { hiddenItems.clear(); - updateHiddenItems(); } finally { - writeLock.unlock(); + hiddenWriteLock.unlock(); } } else { NEIClientConfig.logger.error("Unable to obtain write lock in 'unhideAll'"); } + + calculateVisibility(); } catch (InterruptedException e) { e.printStackTrace(); } } - private static void updateHiddenItems() { - prepareDirtyHiddenItems.restart(); - updateState.restart(); + public static void updateHiddenItems() { + if (ItemList.loadFinished) { + updateState.restart(); + } } public static void loadHidden() { - try { - if (writeLock.tryLock(5, TimeUnit.SECONDS)) { - try { - hiddenItems.clear(); - } finally { - writeLock.unlock(); - } - } else { - NEIClientConfig.logger.error("Unable to obtain write lock in 'loadHidden'"); - } - } catch (InterruptedException e) { - e.printStackTrace(); - return; - } + final List itemList = new LinkedList<>(); - List itemList = new LinkedList<>(); try { - NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10); - for (int i = 0; i < list.tagCount(); i++) + final NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10); + + for (int i = 0; i < list.tagCount(); i++) { itemList.add(ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i))); + } } catch (Exception e) { NEIClientConfig.logger.error("Error loading hiddenItems", e); return; } try { - if (writeLock.tryLock(5, TimeUnit.SECONDS)) { + if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) { try { - for (ItemStack item : itemList) hiddenItems.add(item); + hiddenItems.clear(); + hiddenItems.addAll(itemList); } finally { - writeLock.unlock(); + hiddenWriteLock.unlock(); } } else { NEIClientConfig.logger.error("Unable to obtain second write lock in 'loadHidden'"); @@ -565,171 +558,194 @@ public static void loadHidden() { } catch (InterruptedException e) { e.printStackTrace(); } - updateState.restart(); - } - private static void saveHidden() { - NBTTagList list = dirtyHiddenItems.getAndSet(null); - if (list != null) { - NEIClientConfig.world.nbt.setTag("hiddenItems", list); - } } - private static final RestartableTask prepareDirtyHiddenItems = new RestartableTask("NEI Subset Save Thread") { + public static void saveHidden() { + if (NEIClientConfig.world == null) return; - private List getList() { - try { - if (readLock.tryLock(5, TimeUnit.SECONDS)) { - try { - return hiddenItems.values(); - } finally { - readLock.unlock(); - } - } else { - NEIClientConfig.logger.error("Unable to obtain read lock in 'NEI Subset Save Thread'"); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - return new ArrayList<>(); + final NBTTagList list = new NBTTagList(); + for (ItemStack stack : hiddenItems.values()) { + list.appendTag(stack.writeToNBT(new NBTTagCompound())); } - @Override - public void execute() { - NBTTagList list = new NBTTagList(); - for (ItemStack item : getList()) { - if (interrupted()) return; - NBTTagCompound tag = new NBTTagCompound(); - item.writeToNBT(tag); - list.appendTag(tag); - } - dirtyHiddenItems.set(list); - } - }; + NEIClientConfig.world.nbt.setTag("hiddenItems", list); + } private static final UpdateStateTask updateState = new UpdateStateTask(); private static class UpdateStateTask extends RestartableTask { - private volatile boolean reallocate; - public UpdateStateTask() { super("NEI Subset Item Allocation"); } @Override - public void clearTasks() { - super.clearTasks(); - reallocate = false; - } + public void execute() { - public synchronized void reallocate() { - // System.out.println("NEI Subset - Reallocate"); - // Thread.dumpStack(); - reallocate = true; - restart(); - } + try { + List list = new ArrayList<>(tags.values()); - @Override - public void execute() { - // System.out.println("Executing NEI Subset Item Allocation"); - HashMap state = new HashMap<>(); - List tags = new LinkedList<>(); - synchronized (root) { - cloneStates(root, tags, state); - if (interrupted()) return; - } - - if (reallocate) { - for (ItemStack item : ItemList.items) { - if (interrupted()) return; - for (SubsetTag tag : tags) if (tag.filter.matches(item)) state.get(tag.fullname).items.add(item); - } - } + root.clearCache(); - synchronized (root) { - calculateVisibility(root, state); - if (interrupted()) return; - } + list.parallelStream().forEach(tag -> { + tag.clearCache(); + ItemList.items.stream().filter(tag.filter::matches).map(ItemStack::copy) + .collect(Collectors.toCollection(() -> tag.items)); + }); - subsetState = state; - ItemList.updateFilter.restart(); - } + for (SubsetTag tag : list) { + if (tag.parentPath == null) { + root.children.add(tag); + } else { + tags.get(tag.parentPath).children.add(tag); + } + } - private void cloneStates(SubsetTag tag, List tags, HashMap state) { - for (SubsetTag child : tag.sorted) { - if (interrupted()) return; - cloneStates(child, tags, state); - } + boolean changed = false; + do { + changed = false; - tags.add(tag); - SubsetState sstate = new SubsetState(); - if (!reallocate) sstate.items = SubsetWidget.getState(tag).items; - state.put(tag.fullname, sstate); - } + for (SubsetTag tag : list) { + changed = tag.children.removeIf(child -> child.children.isEmpty() && child.items.isEmpty()) + || changed; + } - private void calculateVisibility(SubsetTag tag, Map state) { - SubsetState sstate = state.get(tag.fullname); - int hidden = 0; - for (SubsetTag child : tag.sorted) { - if (interrupted()) return; - calculateVisibility(child, state); - int cstate = state.get(child.fullname).state; - if (cstate == 1) sstate.state = 1; - else if (cstate == 0) hidden++; - } + } while (changed); - if (sstate.state == 1) return; + for (SubsetTag tag : list) { + tag.children.sort(Comparator.comparing(SubsetTag::displayName)); + tag.calculatedWidth = tag.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0); + } + + root.children.removeIf(child -> child.children.isEmpty() && child.items.isEmpty()); + root.children.sort(Comparator.comparing(SubsetTag::displayName)); + root.calculatedWidth = root.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0); - List items = sstate.items; - for (ItemStack item : items) { - if (interrupted()) return; - if (isHidden(item)) hidden++; + calculateVisibility(root); + } catch (Throwable e) { + e.printStackTrace(); } - if (hidden == tag.sorted.size() + items.size()) sstate.state = 0; - else if (hidden > 0) sstate.state = 1; } } - private long lastclicktime; - public SubsetWidget() { super("NEI Subsets"); API.addItemFilter(this); API.addSearchProvider(new DefaultParserProvider()); - ItemList.loadCallbacks.add(this); + this.z = 1; } @Override public String getRenderLabel() { - return translate("inventory.item_subsets"); + + if (NEIClientConfig.subsetWidgetOnTop()) { + return NEIClientUtils.translate("inventory.item_subsets"); + } else { + return EnumChatFormatting.DARK_PURPLE + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX)) + + EnumChatFormatting.RESET; + } + } @Override public void draw(int mx, int my) { super.draw(mx, my); - area.set(x, y + h, w, LayoutManager.searchField.y - h - y); // 23 for the search box + SubsetWidget.hoverTag = null; + SubsetWidget.hoverStack = null; - hoverStack = null; if (root.isVisible()) { + final Minecraft mc = NEIClientUtils.mc(); + final Rectangle4i screen = new Rectangle4i( + MARGIN, + MARGIN, + mc.currentScreen.width - MARGIN * 2, + mc.currentScreen.height - MARGIN * 2); + final Rectangle4i parent = new Rectangle4i(); + final boolean dropRight = this.x < (screen.x + screen.w / 2); + final boolean dropDown = this.y < (screen.y + screen.h / 2); + + if (dropRight) { + parent.x = this.x; + } else { + parent.x = this.x + this.w; + } + + if (dropDown) { + screen.y = parent.y = this.y + this.h; + screen.h = alignValueToStep(mc.currentScreen.height - MARGIN - screen.y); + } else { + screen.h = alignValueToStep(this.y - screen.y); + screen.y = this.y - screen.h; + parent.y = this.y; + } + + root.resize(screen, parent, dropRight); + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); GuiContainerManager.enable2DRender(); + GuiContainerManager.drawItems.zLevel += 100; - root.resize(area.x, 0, area.y); - root.cacheState(); root.draw(mx, my); + GuiContainerManager.drawItems.zLevel -= 100; GL11.glPopAttrib(); } } + private static int alignValueToStep(int height) { + return (height / SLOT_HEIGHT) * SLOT_HEIGHT; + } + + protected static void calculateVisibility() { + calculateVisibility(root); + ItemList.updateFilter.restart(); + } + + protected static void calculateVisibility(SubsetTag tag) { + // 0 - full hidden + // 1 - partitial hidden + // 2 - enabled + int hidden = 0; + + tag.state = 2; + + for (SubsetTag child : tag.children) { + calculateVisibility(child); + + if (child.state == 1) { + tag.state = 1; + } else if (child.state == 0) { + hidden++; + } + } + + if (tag.state == 1) return; + + for (ItemStack item : tag.items) { + if (isHidden(item)) { + hidden++; + } else if (hidden > 0) { + break; + } + } + + if (hidden == tag.children.size() + tag.items.size()) { + tag.state = 0; + } else if (hidden > 0) { + tag.state = 1; + } + + } + @Override public void update() { + SubsetWidget.enableSearchBySubsets = SearchMode + .fromInt(NEIClientConfig.getIntSetting("inventory.search.subsetsSearchMode")) == SearchMode.PREFIX; Point mouse = GuiDraw.getMousePosition(); updateVisiblity(mouse.x, mouse.y); - saveHidden(); } private void updateVisiblity(int mx, int my) { @@ -737,7 +753,9 @@ private void updateVisiblity(int mx, int my) { root.updateVisiblity(mx, my); - if (!root.isVisible() && bounds().contains(mx, my)) root.setVisible(); + if (!root.isVisible() && bounds().contains(mx, my)) { + root.setVisible(); + } } @Override @@ -753,8 +771,12 @@ public boolean handleClick(int mx, int my, int button) { } if (button == 0) { - if (System.currentTimeMillis() - lastclicktime < 500) unhideAll(); - else root.setVisible(); + + if (System.currentTimeMillis() - lastclicktime < 500) { + unhideAll(); + } else { + root.setVisible(); + } NEIClientUtils.playClickSound(); lastclicktime = System.currentTimeMillis(); @@ -773,7 +795,9 @@ public boolean onButtonPress(boolean rightclick) { @Override public void mouseDragged(int mx, int my, int button, long heldTime) { - if (root.isVisible()) root.mouseDragged(mx, my, button, heldTime); + if (root.isVisible()) { + root.mouseDragged(mx, my, button, heldTime); + } } @Override @@ -783,42 +807,71 @@ public void mouseUp(int mx, int my, int button) { @Override public boolean onMouseWheel(int i, int mx, int my) { - return root.isVisible() && root.mouseScrolled(mx, my, -i); + return root.isVisible() && root.mouseScrolled(mx, my, -i) || contains(mx, my); } @Override public void onGuiClick(int mx, int my) { - if (!contains(mx, my)) root.setHidden(); + if (!contains(mx, my)) { + root.setHidden(); + } } @Override public ItemStack getStackMouseOver(int mx, int my) { - return hoverStack; + return SubsetWidget.hoverStack; } @Override public ItemFilter getFilter() { - // synchronise access on hiddenItems - return item -> { - try { - if (readLock.tryLock(5, TimeUnit.SECONDS)) { - try { - return !hiddenItems.matches(item); - } finally { - readLock.unlock(); - } - } else { - NEIClientConfig.logger.error("Unable to obtain read lock in 'getFilter'"); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - return false; - }; + return item -> !isHidden(item); + } + + @Override + public void addTooltips(List tooltip) { + if (SubsetWidget.hoverStack == null && SubsetWidget.hoverTag != null) { + tooltip.add(SubsetWidget.hoverTag.displayName() + GuiDraw.TOOLTIP_LINESPACE); + } } @Override - public void itemsLoaded() { - updateState.reallocate(); + public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { + + if (SubsetWidget.hoverStack != null) { + + if (NEIClientConfig.canCheatItem(SubsetWidget.hoverStack)) { + hotkeys.put( + NEIClientUtils.translate("subsets.item.cheat.key"), + NEIClientUtils.translate("subsets.item.cheat")); + } + + if (SubsetWidget.enableSearchBySubsets) { + hotkeys.put( + NEIClientUtils.translate("subsets.item.search.key"), + NEIClientUtils.translate("subsets.item.search")); + } + + hotkeys.put( + NEIClientUtils.translate("subsets.item.show.key"), + NEIClientUtils.translate("subsets.item.show")); + hotkeys.put( + NEIClientUtils.translate("subsets.item.hide.key"), + NEIClientUtils.translate("subsets.item.hide")); + } else if (SubsetWidget.hoverTag != null) { + hotkeys.put( + NEIClientUtils.translate("subsets.tag.onlythis.key"), + NEIClientUtils.translate("subsets.tag.onlythis")); + + if (SubsetWidget.enableSearchBySubsets) { + hotkeys.put( + NEIClientUtils.translate("subsets.tag.search.key"), + NEIClientUtils.translate("subsets.tag.search")); + } + + hotkeys.put(NEIClientUtils.translate("subsets.tag.show.key"), NEIClientUtils.translate("subsets.tag.show")); + hotkeys.put(NEIClientUtils.translate("subsets.tag.hide.key"), NEIClientUtils.translate("subsets.tag.hide")); + } + + return hotkeys; } } diff --git a/src/main/java/codechicken/nei/WidgetZOrder.java b/src/main/java/codechicken/nei/WidgetZOrder.java index 86259f897..42b0627b9 100644 --- a/src/main/java/codechicken/nei/WidgetZOrder.java +++ b/src/main/java/codechicken/nei/WidgetZOrder.java @@ -11,6 +11,6 @@ public WidgetZOrder(boolean topfirst) { } public int compare(Widget w1, Widget w2) { - return w1.z != w2.z ? ((topfirst ? w1.z > w2.z : w1.z < w2.z) ? 1 : -1) : 1; + return w1.z != w2.z ? ((topfirst ? w1.z < w2.z : w1.z > w2.z) ? 1 : -1) : 1; } } diff --git a/src/main/java/codechicken/nei/api/ItemInfo.java b/src/main/java/codechicken/nei/api/ItemInfo.java index 1ccdccd5e..cc0520e3e 100644 --- a/src/main/java/codechicken/nei/api/ItemInfo.java +++ b/src/main/java/codechicken/nei/api/ItemInfo.java @@ -43,7 +43,6 @@ import codechicken.core.featurehack.GameDataManipulator; import codechicken.nei.InfiniteStackSizeHandler; import codechicken.nei.InfiniteToolHandler; -import codechicken.nei.ItemList; import codechicken.nei.ItemList.AnyMultiItemFilter; import codechicken.nei.ItemList.PatternItemFilter; import codechicken.nei.ItemMobSpawner; @@ -136,7 +135,6 @@ public static void load(World world) { addIDDumps(); addSearchProviders(); PresetsList.load(); - ItemList.loadCallbacks.add(TooltipFilter::populateSearchMap); } private static void addSearchProviders() { diff --git a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java index 555d7499c..da6f50651 100644 --- a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java +++ b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java @@ -40,12 +40,11 @@ import org.lwjgl.opengl.GL11; import codechicken.lib.gui.GuiDraw; +import codechicken.nei.ItemList; import codechicken.nei.ItemStackSet; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; -import codechicken.nei.SearchField; import codechicken.nei.recipe.StackInfo; -import codechicken.nei.search.TooltipFilter; import codechicken.nei.util.ReadableNumberConverter; public class GuiContainerManager { @@ -55,8 +54,7 @@ private static class ResourcePackReloaded implements IResourceManagerReloadListe @Override public void onResourceManagerReload(IResourceManager p_110549_1_) { renderingErrorItems.clear(); - TooltipFilter.populateSearchMap(); - SearchField.searchParser.clearCache(); + ItemList.loadItems.restart(); } } @@ -417,9 +415,9 @@ public static void enable2DRender() { public GuiContainerManager(GuiContainer screen) { window = screen; - if (screen instanceof IContainerTooltipHandler) { + if (screen instanceof IContainerTooltipHandler tooltipHandler) { instanceTooltipHandlers = Collections.synchronizedList(new LinkedList<>()); - instanceTooltipHandlers.add((IContainerTooltipHandler) screen); + instanceTooltipHandlers.add(tooltipHandler); instanceTooltipHandlers.addAll(tooltipHandlers); } else instanceTooltipHandlers = tooltipHandlers; } diff --git a/src/main/java/codechicken/nei/search/TooltipFilter.java b/src/main/java/codechicken/nei/search/TooltipFilter.java index 30495161e..9a035fe25 100644 --- a/src/main/java/codechicken/nei/search/TooltipFilter.java +++ b/src/main/java/codechicken/nei/search/TooltipFilter.java @@ -6,7 +6,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; -import codechicken.nei.ItemList; import codechicken.nei.ItemStackMap; import codechicken.nei.NEIClientUtils; import codechicken.nei.api.ItemFilter; @@ -26,14 +25,18 @@ public boolean matches(ItemStack itemStack) { return pattern.matcher(getSearchTooltip(itemStack)).find(); } - public static void populateSearchMap() { + public static void clearCache() { itemSearchNames.clear(); - new Thread( - () -> ItemList.items.parallelStream().forEach(TooltipFilter::getSearchTooltip), - "NEI populate Tooltip Filter").start(); } - protected static String getSearchTooltip(ItemStack stack) { + public static void putItem(ItemStack stack) { + String tooltip = getTooltip(stack.copy()); + synchronized (itemSearchNames) { + itemSearchNames.put(stack, tooltip); + } + } + + public static String getSearchTooltip(ItemStack stack) { String tooltip = itemSearchNames.get(stack); if (tooltip == null) { diff --git a/src/main/resources/assets/nei/lang/en_US.lang b/src/main/resources/assets/nei/lang/en_US.lang index d2b9c2977..e04f94fb0 100644 --- a/src/main/resources/assets/nei/lang/en_US.lang +++ b/src/main/resources/assets/nei/lang/en_US.lang @@ -327,6 +327,13 @@ nei.options.inventory.history.useRows=Rows nei.options.inventory.history.splittingMode=Splitting Mode nei.options.inventory.history.splittingMode.0=Background nei.options.inventory.history.splittingMode.1=Dotted Line +nei.options.inventory.subsets=Subsets Widget +nei.options.inventory.subsets.enabled=Subsets Visibility +nei.options.inventory.subsets.enabled.true=Enabled +nei.options.inventory.subsets.enabled.false=Disabled +nei.options.inventory.subsets.widgetPosition=Widget Position +nei.options.inventory.subsets.widgetPosition.true=Top +nei.options.inventory.subsets.widgetPosition.false=Bottom nei.options.inventory.itemzoom=Item Zoom Panel nei.options.inventory.itemzoom.enabled=Item Zoom Panel Visibility nei.options.inventory.itemzoom.enabled.true=Enabled @@ -449,6 +456,23 @@ nei.itemsort.default.tip=Leave the natural ordering of each item unchanged nei.itemsort.damage=Item Damage nei.itemsort.name=Display Name +nei.subsets.item.cheat=Give out an Item +nei.subsets.item.cheat.key=Ctrl + LMB +nei.subsets.item.search=Search by Item +nei.subsets.item.search.key=Shift + LMB +nei.subsets.item.show=Show Item +nei.subsets.item.show.key=LMB +nei.subsets.item.hide=Hide Item +nei.subsets.item.hide.key=RMB +nei.subsets.tag.search=Search by Subset +nei.subsets.tag.search.key=Shift + LMB +nei.subsets.tag.onlythis=Show only this Subset +nei.subsets.tag.onlythis.key=Double LMB +nei.subsets.tag.show=Show Subset +nei.subsets.tag.show.key=LMB +nei.subsets.tag.hide=Hide Subset +nei.subsets.tag.hide.key=RMB + nei.subsets.Blocks=Blocks nei.subsets.Blocks.MobSpawners=Mob Spawners nei.subsets.CreativeTabs=Creative Tabs