From 66abc317e5fc36a397ca1cc919e388fbe143956b Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 1 Apr 2020 20:01:34 +0800 Subject: ScrollingContainer & SubsetsMenu && 20w18b Signed-off-by: shedaniel --- .../me/shedaniel/rei/RoughlyEnoughItemsCore.java | 27 ++- .../java/me/shedaniel/rei/api/ConfigObject.java | 3 + .../shedaniel/rei/api/subsets/SubsetsRegistry.java | 40 ++++ .../shedaniel/rei/gui/ContainerScreenOverlay.java | 98 ++++++-- .../rei/gui/VillagerRecipeViewingScreen.java | 132 +++-------- .../rei/gui/config/entry/FilteringEntry.java | 175 ++++---------- .../me/shedaniel/rei/gui/subsets/SubsetsMenu.java | 256 +++++++++++++++++++++ .../rei/gui/subsets/SubsetsMenuEntry.java | 21 ++ .../gui/subsets/entries/EntryStackMenuEntry.java | 117 ++++++++++ .../rei/gui/subsets/entries/SubMenuEntry.java | 209 +++++++++++++++++ .../shedaniel/rei/gui/widget/EntryListWidget.java | 164 +++---------- .../me/shedaniel/rei/gui/widget/EntryWidget.java | 10 +- .../rei/gui/widget/FavoritesListWidget.java | 159 +++---------- .../shedaniel/rei/gui/widget/LateRenderable.java | 1 - .../rei/gui/widget/ScrollingContainer.java | 180 +++++++++++++++ .../me/shedaniel/rei/impl/ClientHelperImpl.java | 4 +- .../me/shedaniel/rei/impl/ConfigObjectImpl.java | 35 ++- .../me/shedaniel/rei/impl/EntryRegistryImpl.java | 3 +- .../me/shedaniel/rei/impl/InternalWidgets.java | 80 ++++++- .../me/shedaniel/rei/impl/RecipeHelperImpl.java | 3 + .../java/me/shedaniel/rei/impl/SearchArgument.java | 7 + .../rei/impl/subsets/SubsetsRegistryImpl.java | 83 +++++++ .../me/shedaniel/rei/plugin/DefaultPlugin.java | 22 +- .../plugin/beacon/DefaultBeaconBaseCategory.java | 140 +++-------- .../information/DefaultInformationCategory.java | 143 +++--------- .../assets/roughlyenoughitems/lang/en_us.json | 6 + 26 files changed, 1329 insertions(+), 789 deletions(-) create mode 100644 src/main/java/me/shedaniel/rei/api/subsets/SubsetsRegistry.java create mode 100644 src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenu.java create mode 100644 src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenuEntry.java create mode 100644 src/main/java/me/shedaniel/rei/gui/subsets/entries/EntryStackMenuEntry.java create mode 100644 src/main/java/me/shedaniel/rei/gui/subsets/entries/SubMenuEntry.java create mode 100644 src/main/java/me/shedaniel/rei/gui/widget/ScrollingContainer.java create mode 100644 src/main/java/me/shedaniel/rei/impl/subsets/SubsetsRegistryImpl.java (limited to 'src') diff --git a/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java b/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java index 617f246b0..34ada2efb 100644 --- a/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java +++ b/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java @@ -63,10 +63,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.ApiStatus; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -81,6 +78,8 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer { private static final Map plugins = Maps.newHashMap(); private static final ExecutorService SYNC_RECIPES = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "REI-SyncRecipes")); private static ConfigManager configManager; + @ApiStatus.Experimental + public static boolean isLeftModePressed = false; @ApiStatus.Internal public static RecipeHelper getRecipeHelper() { @@ -248,12 +247,15 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer { } private boolean shouldReturn(Class screen) { - for (OverlayDecider decider : DisplayHelper.getInstance().getAllOverlayDeciders()) { - if (!decider.isHandingScreen(screen)) - continue; - ActionResult result = decider.shouldScreenBeOverlayed(screen); - if (result != ActionResult.PASS) - return result == ActionResult.FAIL || ScreenHelper.getLastHandledScreen() == null; + try { + for (OverlayDecider decider : DisplayHelper.getInstance().getAllOverlayDeciders()) { + if (!decider.isHandingScreen(screen)) + continue; + ActionResult result = decider.shouldScreenBeOverlayed(screen); + if (result != ActionResult.PASS) + return result == ActionResult.FAIL || ScreenHelper.getLastHandledScreen() == null; + } + } catch (ConcurrentModificationException ignored) { } return true; } @@ -298,6 +300,7 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer { return ActionResult.PASS; }); ClothClientHooks.SCREEN_MOUSE_CLICKED.register((minecraftClient, screen, v, v1, i) -> { + isLeftModePressed = true; if (screen instanceof CreativeInventoryScreen) if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().mouseClicked(v, v1, i)) { screen.setFocused(ScreenHelper.getLastOverlay()); @@ -307,6 +310,10 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer { } return ActionResult.PASS; }); + ClothClientHooks.SCREEN_MOUSE_RELEASED.register((minecraftClient, screen, v, v1, i) -> { + isLeftModePressed = false; + return ActionResult.PASS; + }); ClothClientHooks.SCREEN_MOUSE_SCROLLED.register((minecraftClient, screen, v, v1, v2) -> { if (shouldReturn(screen.getClass())) return ActionResult.PASS; diff --git a/src/main/java/me/shedaniel/rei/api/ConfigObject.java b/src/main/java/me/shedaniel/rei/api/ConfigObject.java index 5070d2da0..08a70ab04 100644 --- a/src/main/java/me/shedaniel/rei/api/ConfigObject.java +++ b/src/main/java/me/shedaniel/rei/api/ConfigObject.java @@ -152,4 +152,7 @@ public interface ConfigObject { @ApiStatus.Experimental boolean doDebugSearchTimeRequired(); + + @ApiStatus.Experimental + boolean isSubsetsEnabled(); } diff --git a/src/main/java/me/shedaniel/rei/api/subsets/SubsetsRegistry.java b/src/main/java/me/shedaniel/rei/api/subsets/SubsetsRegistry.java new file mode 100644 index 000000000..3c7e46304 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/api/subsets/SubsetsRegistry.java @@ -0,0 +1,40 @@ +package me.shedaniel.rei.api.subsets; + +import me.shedaniel.rei.api.EntryStack; +import me.shedaniel.rei.impl.subsets.SubsetsRegistryImpl; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +@ApiStatus.Experimental +public interface SubsetsRegistry { + SubsetsRegistry INSTANCE = new SubsetsRegistryImpl(); + + /** + * Gets all paths an entry is in, note that this is a really slow call as it looks through all paths. + */ + @NotNull + List getEntryPaths(@NotNull EntryStack stack); + + @Nullable + Set getPathEntries(@NotNull String path); + + @NotNull + Set getOrCreatePathEntries(@NotNull String path); + + @NotNull + Set getPaths(); + + void registerPathEntry(@NotNull String path, @NotNull EntryStack stack); + + void registerPathEntries(@NotNull String path, @NotNull Collection stacks); + + default void registerPathEntries(@NotNull String path, @NotNull EntryStack... stacks) { + registerPathEntries(path, Arrays.asList(stacks)); + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java b/src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java index 82bd199e5..fc7f8f950 100644 --- a/src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java +++ b/src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java @@ -34,7 +34,9 @@ import me.shedaniel.rei.api.widgets.Button; import me.shedaniel.rei.api.widgets.Tooltip; import me.shedaniel.rei.api.widgets.Widgets; import me.shedaniel.rei.gui.config.SearchFieldLocation; +import me.shedaniel.rei.gui.subsets.SubsetsMenu; import me.shedaniel.rei.gui.widget.*; +import me.shedaniel.rei.impl.ClientHelperImpl; import me.shedaniel.rei.impl.InternalWidgets; import me.shedaniel.rei.impl.ScreenHelper; import me.shedaniel.rei.impl.Weather; @@ -110,8 +112,13 @@ public class ContainerScreenOverlay extends WidgetWithBounds { }; private Rectangle bounds; private Window window; - private List lateRenderables = Lists.newArrayList(); private Button leftButton, rightButton; + @ApiStatus.Experimental + private Rectangle subsetsButtonBounds; + @ApiStatus.Experimental + @Nullable + private SubsetsMenu subsetsMenu = null; + private Widget wrappedSubsetsMenu = null; public static EntryListWidget getEntryListWidget() { return ENTRY_LIST_WIDGET; @@ -122,6 +129,12 @@ public class ContainerScreenOverlay extends WidgetWithBounds { return favoritesListWidget; } + @ApiStatus.Experimental + @Nullable + public SubsetsMenu getSubsetsMenu() { + return subsetsMenu; + } + public void init(boolean useless) { init(); } @@ -130,6 +143,8 @@ public class ContainerScreenOverlay extends WidgetWithBounds { this.shouldReInit = false; //Update Variables this.children().clear(); + this.wrappedSubsetsMenu = null; + this.subsetsMenu = null; this.window = MinecraftClient.getInstance().getWindow(); @SuppressWarnings({"RawTypeCanBeGeneric", "rawtypes"}) DisplayHelper.DisplayBoundsHandler boundsHandler = DisplayHelper.getInstance().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass()); @@ -171,8 +186,8 @@ public class ContainerScreenOverlay extends WidgetWithBounds { } final Rectangle configButtonArea = getConfigButtonArea(); - LateRenderable tmp; - widgets.add((Widget) (tmp = InternalWidgets.wrapLateRenderable(InternalWidgets.mergeWidgets( + Widget tmp; + widgets.add(tmp = InternalWidgets.wrapLateRenderable(InternalWidgets.mergeWidgets( Widgets.createButton(configButtonArea, NarratorManager.EMPTY) .onClick(button -> { if (Screen.hasShiftDown()) { @@ -209,17 +224,8 @@ public class ContainerScreenOverlay extends WidgetWithBounds { helper.drawTexture(configButtonArea.x + 3, configButtonArea.y + 3, 0, 0, 14, 14); }) ) - ))); - ((Widget) tmp).setZ(600); - lateRenderables.add(tmp); -// widgets.add((Widget) (tmp = InternalWidgets.wrapLateRenderable(Widgets.createTexturedWidget(CHEST_GUI_TEXTURE, configButtonArea.x + 3, configButtonArea.y + 3, 0, 0, 14, 14)))); -// widgets.add((Widget) (tmp = InternalWidgets.wrapLateRenderable(Widgets.createDrawableWidget((helper, mouseX, mouseY, delta) -> { -// RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); -// MinecraftClient.getInstance().getTextureManager().bindTexture(CHEST_GUI_TEXTURE); -// helper.blit(configButtonArea.x + 3, configButtonArea.y + 3, 0, 0, 14, 14); -// })))); -// ((Widget) tmp).setZ(600); -// lateRenderables.add(tmp); + )); + tmp.setZ(600); if (ConfigObject.getInstance().doesShowUtilsButtons()) { widgets.add(Widgets.createButton(ConfigObject.getInstance().isLowerConfigButton() ? new Rectangle(ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10, 10, 20, 20) : new Rectangle(ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 55 : 35, 10, 20, 20), NarratorManager.EMPTY) .onClick(button -> MinecraftClient.getInstance().player.sendChatMessage(ConfigObject.getInstance().getGamemodeCommand().replaceAll("\\{gamemode}", getNextGameMode(Screen.hasShiftDown()).getName()))) @@ -243,6 +249,20 @@ public class ContainerScreenOverlay extends WidgetWithBounds { xxx += ConfigObject.getInstance().isLeftHandSidePanel() ? -25 : 25; } } + subsetsButtonBounds = getSubsetsButtonBounds(); + if (ConfigObject.getInstance().isSubsetsEnabled()) { + widgets.add(InternalWidgets.wrapLateRenderable(Widgets.createButton(subsetsButtonBounds, ((ClientHelperImpl) ClientHelper.getInstance()).isAprilFools.get() ? I18n.translate("text.rei.tiny_potato") : I18n.translate("text.rei.subsets")) + .onClick(button -> { + if (subsetsMenu == null) { + wrappedSubsetsMenu = InternalWidgets.wrapTranslate(InternalWidgets.wrapLateRenderable(this.subsetsMenu = SubsetsMenu.createFromRegistry(new Point(this.subsetsButtonBounds.x, this.subsetsButtonBounds.getMaxY()))), 0, 0, 400); + this.widgets.add(this.wrappedSubsetsMenu); + } else { + this.widgets.remove(this.wrappedSubsetsMenu); + this.subsetsMenu = null; + this.wrappedSubsetsMenu = null; + } + }))); + } if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) { widgets.add(Widgets.createClickableLabel(new Point(bounds.x + (bounds.width / 2), bounds.y + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + 10), "", label -> { ENTRY_LIST_WIDGET.setPage(0); @@ -256,7 +276,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds { Rectangle area = getCraftableToggleArea(); ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer(); ItemStack icon = new ItemStack(Blocks.CRAFTING_TABLE); - this.widgets.add((Widget) (tmp = InternalWidgets.wrapLateRenderable(InternalWidgets.mergeWidgets( + this.widgets.add(tmp = InternalWidgets.wrapLateRenderable(InternalWidgets.mergeWidgets( Widgets.createButton(area, NarratorManager.EMPTY) .focusable(false) .onClick(button -> { @@ -271,12 +291,27 @@ public class ContainerScreenOverlay extends WidgetWithBounds { itemRenderer.renderGuiItemIcon(icon, area.x + 2, area.y + 2); itemRenderer.zOffset = 0.0F; })) - ))); - ((Widget) tmp).setZ(600); - lateRenderables.add(tmp); + )); + tmp.setZ(600); } } + @ApiStatus.Experimental + private Rectangle getSubsetsButtonBounds() { + if (ConfigObject.getInstance().isSubsetsEnabled()) { + if (MinecraftClient.getInstance().currentScreen instanceof RecipeViewingScreen) { + RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen; + return new Rectangle(widget.getBounds().x, 3, widget.getBounds().width, 18); + } + if (MinecraftClient.getInstance().currentScreen instanceof VillagerRecipeViewingScreen) { + VillagerRecipeViewingScreen widget = (VillagerRecipeViewingScreen) MinecraftClient.getInstance().currentScreen; + return new Rectangle(widget.bounds.x, 3, widget.bounds.width, 18); + } + return new Rectangle(((ContainerScreenHooks) ScreenHelper.getLastHandledScreen()).rei_getContainerLeft(), 3, ((ContainerScreenHooks) ScreenHelper.getLastHandledScreen()).rei_getContainerWidth(), 18); + } + return null; + } + private Weather getNextWeather() { try { Weather current = getCurrentWeather(); @@ -417,10 +452,15 @@ public class ContainerScreenOverlay extends WidgetWithBounds { public void lateRender(int mouseX, int mouseY, float delta) { if (ScreenHelper.isOverlayVisible()) { ScreenHelper.getSearchField().laterRender(mouseX, mouseY, delta); - for (LateRenderable lateRenderable : lateRenderables) { - lateRenderable.lateRender(mouseX, mouseY, delta); + for (Widget widget : widgets) { + if (widget instanceof LateRenderable && wrappedSubsetsMenu != widget) + widget.render(mouseX, mouseY, delta); } } + if (wrappedSubsetsMenu != null) { + TOOLTIPS.clear(); + wrappedSubsetsMenu.render(mouseX, mouseY, delta); + } Screen currentScreen = MinecraftClient.getInstance().currentScreen; if (!(currentScreen instanceof RecipeViewingScreen) || !((RecipeViewingScreen) currentScreen).choosePageActivated) for (Tooltip tooltip : TOOLTIPS) { @@ -462,7 +502,8 @@ public class ContainerScreenOverlay extends WidgetWithBounds { rightButton.setEnabled(ENTRY_LIST_WIDGET.getTotalPages() > 1); } for (Widget widget : widgets) { - widget.render(int_1, int_2, float_1); + if (!(widget instanceof LateRenderable)) + widget.render(int_1, int_2, float_1); } } @@ -470,6 +511,8 @@ public class ContainerScreenOverlay extends WidgetWithBounds { public boolean mouseScrolled(double i, double j, double amount) { if (!ScreenHelper.isOverlayVisible()) return false; + if (wrappedSubsetsMenu != null && wrappedSubsetsMenu.mouseScrolled(i, j, amount)) + return true; if (isInside(PointHelper.ofMouse())) { if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) { if (amount > 0 && leftButton.isEnabled()) @@ -487,7 +530,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds { return true; } for (Widget widget : widgets) - if (widget != ENTRY_LIST_WIDGET && (favoritesListWidget == null || widget != favoritesListWidget) && widget.mouseScrolled(i, j, amount)) + if (widget != ENTRY_LIST_WIDGET && (favoritesListWidget == null || widget != favoritesListWidget) && (wrappedSubsetsMenu == null || widget != wrappedSubsetsMenu) && widget.mouseScrolled(i, j, amount)) return true; return false; } @@ -540,7 +583,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds { } @Override - public List children() { + public List children() { return widgets; } @@ -548,6 +591,13 @@ public class ContainerScreenOverlay extends WidgetWithBounds { public boolean mouseClicked(double double_1, double double_2, int int_1) { if (!ScreenHelper.isOverlayVisible()) return false; + if (wrappedSubsetsMenu != null && wrappedSubsetsMenu.mouseClicked(double_1, double_2, int_1)) { + this.setFocused(wrappedSubsetsMenu); + if (int_1 == 0) + this.setDragging(true); + ScreenHelper.getSearchField().setFocused(false); + return true; + } if (MinecraftClient.getInstance().currentScreen instanceof HandledScreen && ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) { ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen; for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas()) @@ -559,7 +609,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds { } } for (Element element : widgets) - if (element.mouseClicked(double_1, double_2, int_1)) { + if (element != wrappedSubsetsMenu && element.mouseClicked(double_1, double_2, int_1)) { this.setFocused(element); if (int_1 == 0) this.setDragging(true); diff --git a/src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java b/src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java index b6f28ee09..6c5e91278 100644 --- a/src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java +++ b/src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java @@ -35,6 +35,7 @@ import me.shedaniel.rei.api.widgets.Button; import me.shedaniel.rei.api.widgets.Tooltip; import me.shedaniel.rei.api.widgets.Widgets; import me.shedaniel.rei.gui.entries.RecipeEntry; +import me.shedaniel.rei.gui.widget.ScrollingContainer; import me.shedaniel.rei.gui.widget.TabWidget; import me.shedaniel.rei.gui.widget.Widget; import me.shedaniel.rei.impl.ClientHelperImpl; @@ -44,9 +45,6 @@ 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.render.BufferBuilder; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexFormats; import net.minecraft.client.resource.language.I18n; import net.minecraft.client.sound.PositionedSoundInstance; import net.minecraft.client.util.NarratorManager; @@ -76,14 +74,24 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen private int tabsPerPage = 8; private int selectedCategoryIndex = 0; private int selectedRecipeIndex = 0; - private double scrollAmount = 0; - private double target; - private long start; - private long duration; + private final ScrollingContainer scrolling = new ScrollingContainer() { + @Override + public Rectangle getBounds() { + return new Rectangle(scrollListBounds.x + 1, scrollListBounds.y + 1, scrollListBounds.width - 2, scrollListBounds.height - 2); + } + + @Override + public int getMaxScrollHeight() { + int i = 0; + for (Button button : buttonList) { + i += button.getBounds().height; + } + return i; + } + }; private float scrollBarAlpha = 0; private float scrollBarAlphaFuture = 0; private long scrollBarAlphaFutureTime = -1; - private boolean draggingScrollBar = false; private int tabsPage = -1; private EntryStack ingredientStackToNotice = EntryStack.empty(); private EntryStack resultStackToNotice = EntryStack.empty(); @@ -132,7 +140,7 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen super.init(); boolean isCompactTabs = ConfigObject.getInstance().isUsingCompactTabs(); int tabSize = isCompactTabs ? 24 : 28; - this.draggingScrollBar = false; + scrolling.draggingScrollBar = false; this.children.clear(); this.widgets.clear(); this.buttonList.clear(); @@ -246,23 +254,12 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen ScreenHelper.getLastOverlay().init(); } - private double getMaxScroll() { - return Math.max(0, this.getMaxScrollPosition() - (scrollListBounds.height - 2)); - } - @Override public boolean mouseClicked(double mouseX, double mouseY, int int_1) { - double height = getMaxScrollPosition(); - int actualHeight = scrollListBounds.height - 2; - if (height > actualHeight && scrollBarAlpha > 0 && mouseY >= scrollListBounds.y + 1 && mouseY <= scrollListBounds.getMaxY() - 1) { - double scrollbarPositionMinX = scrollListBounds.getMaxX() - 6; - if (mouseX >= scrollbarPositionMinX & mouseX <= scrollbarPositionMinX + 8) { - this.draggingScrollBar = true; - scrollBarAlpha = 1; - return false; - } + if (scrolling.updateDraggingState(mouseX, mouseY, int_1)) { + scrollBarAlpha = 1; + return true; } - this.draggingScrollBar = false; return super.mouseClicked(mouseX, mouseY, int_1); } @@ -274,29 +271,11 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen return super.charTyped(char_1, int_1); } - public void offset(double value, boolean animated) { - scrollTo(target + value, animated); - } - - public void scrollTo(double value, boolean animated) { - scrollTo(value, animated, ClothConfigInitializer.getScrollDuration()); - } - - public void scrollTo(double value, boolean animated, long duration) { - target = ClothConfigInitializer.clamp(value, getMaxScroll()); - - if (animated) { - start = System.currentTimeMillis(); - this.duration = duration; - } else - scrollAmount = target; - } - @Override public boolean mouseScrolled(double double_1, double double_2, double double_3) { - double height = CollectionUtils.sumInt(buttonList, b -> b.getBounds().getHeight()); + double height = scrolling.getMaxScrollHeight(); if (scrollListBounds.contains(double_1, double_2) && height > scrollListBounds.height - 2) { - offset(ClothConfigInitializer.getScrollStep() * -double_3, true); + scrolling.offset(ClothConfigInitializer.getScrollStep() * -double_3, true); if (scrollBarAlphaFuture == 0) scrollBarAlphaFuture = 1f; if (System.currentTimeMillis() - scrollBarAlphaFutureTime > 300f) @@ -323,10 +302,6 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen return super.mouseScrolled(double_1, double_2, double_3); } - private double getMaxScrollPosition() { - return CollectionUtils.sumInt(buttonList, b -> b.getBounds().getHeight()); - } - @Override public void render(int mouseX, int mouseY, float delta) { if (ConfigObject.getInstance().doesVillagerScreenHavePermanentScrollBar()) { @@ -351,7 +326,7 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen scrollBarAlpha = Math.max(Math.min(1f, l / 300f), scrollBarAlpha); } } - updatePosition(delta); + scrolling.updatePosition(delta); this.fillGradient(0, 0, this.width, this.height, -1072689136, -804253680); int yOffset = 0; for (Widget widget : widgets) { @@ -359,9 +334,9 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen } ScreenHelper.getLastOverlay().render(mouseX, mouseY, delta); RenderSystem.pushMatrix(); - ScissorsHandler.INSTANCE.scissor(new Rectangle(0, scrollListBounds.y + 1, width, scrollListBounds.height - 2)); + ScissorsHandler.INSTANCE.scissor(scrolling.getBounds()); for (Button button : buttonList) { - button.getBounds().y = scrollListBounds.y + 1 + yOffset - (int) scrollAmount; + button.getBounds().y = scrollListBounds.y + 1 + yOffset - (int) scrolling.scrollAmount; if (button.getBounds().getMaxY() > scrollListBounds.getMinY() && button.getBounds().getMinY() < scrollListBounds.getMaxY()) { button.render(mouseX, mouseY, delta); } @@ -374,65 +349,18 @@ public class VillagerRecipeViewingScreen extends Screen implements RecipeScreen Optional.ofNullable(recipeRenderers.get(i).getTooltip(new Point(mouseX, mouseY))).ifPresent(Tooltip::queue); } } - double maxScroll = getMaxScrollPosition(); - if (maxScroll > scrollListBounds.height - 2) { - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder buffer = tessellator.getBuffer(); - int height = (int) (((scrollListBounds.height - 2) * (scrollListBounds.height - 2)) / this.getMaxScrollPosition()); - height = MathHelper.clamp(height, 32, scrollListBounds.height - 2 - 8); - height -= Math.min((scrollAmount < 0 ? (int) -scrollAmount : scrollAmount > getMaxScroll() ? (int) scrollAmount - getMaxScroll() : 0), height * .95); - height = Math.max(10, height); - int minY = (int) Math.min(Math.max((int) scrollAmount * (scrollListBounds.height - 2 - height) / getMaxScroll() + scrollListBounds.y + 1, scrollListBounds.y + 1), scrollListBounds.getMaxY() - 1 - height); - int scrollbarPositionMinX = scrollListBounds.getMaxX() - 6, scrollbarPositionMaxX = scrollListBounds.getMaxX() - 1; - boolean hovered = (new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height)).contains(PointHelper.ofMouse()); - float bottomC = (hovered ? .67f : .5f) * (REIHelper.getInstance().isDarkThemeEnabled() ? 0.8f : 1f); - float topC = (hovered ? .87f : .67f) * (REIHelper.getInstance().isDarkThemeEnabled() ? 0.8f : 1f); - RenderSystem.disableTexture(); - RenderSystem.enableBlend(); - RenderSystem.disableAlphaTest(); - RenderSystem.blendFuncSeparate(770, 771, 1, 0); - RenderSystem.shadeModel(7425); - buffer.begin(7, VertexFormats.POSITION_COLOR); - buffer.vertex(scrollbarPositionMinX, minY + height, 800).color(bottomC, bottomC, bottomC, scrollBarAlpha).next(); - buffer.vertex(scrollbarPositionMaxX, minY + height, 800).color(bottomC, bottomC, bottomC, scrollBarAlpha).next(); - buffer.vertex(scrollbarPositionMaxX, minY, 800).color(bottomC, bottomC, bottomC, scrollBarAlpha).next(); - buffer.vertex(scrollbarPositionMinX, minY, 800).color(bottomC, bottomC, bottomC, scrollBarAlpha).next(); - tessellator.draw(); - buffer.begin(7, VertexFormats.POSITION_COLOR); - buffer.vertex(scrollbarPositionMinX, minY + height - 1, 800).color(topC, topC, topC, scrollBarAlpha).next(); - buffer.vertex(scrollbarPositionMaxX - 1, minY + height - 1, 800).color(topC, topC, topC, scrollBarAlpha).next(); - buffer.vertex(scrollbarPositionMaxX - 1, minY, 800).color(topC, topC, topC, scrollBarAlpha).next(); - buffer.vertex(scrollbarPositionMinX, minY, 800).color(topC, topC, topC, scrollBarAlpha).next(); - tessellator.draw(); - RenderSystem.shadeModel(7424); - RenderSystem.disableBlend(); - RenderSystem.enableAlphaTest(); - RenderSystem.enableTexture(); - } + scrolling.renderScrollBar(0, scrollBarAlpha); ScissorsHandler.INSTANCE.removeLastScissor(); RenderSystem.popMatrix(); ScreenHelper.getLastOverlay().lateRender(mouseX, mouseY, delta); } - private void updatePosition(float delta) { - double[] target = new double[]{this.target}; - this.scrollAmount = ClothConfigInitializer.handleScrollingPosition(target, this.scrollAmount, this.getMaxScroll(), delta, this.start, this.duration); - this.target = target[0]; - } - @Override public boolean mouseDragged(double mouseX, double mouseY, int int_1, double double_3, double double_4) { - if (int_1 == 0 && scrollBarAlpha > 0 && draggingScrollBar) { - double height = CollectionUtils.sumInt(buttonList, b -> b.getBounds().getHeight()); - int actualHeight = scrollListBounds.height - 2; - if (height > actualHeight && mouseY >= scrollListBounds.y + 1 && mouseY <= scrollListBounds.getMaxY() - 1) { - int int_3 = MathHelper.clamp((int) ((actualHeight * actualHeight) / height), 32, actualHeight - 8); - double double_6 = Math.max(1.0D, Math.max(1d, height) / (double) (actualHeight - int_3)); - scrollBarAlphaFutureTime = System.currentTimeMillis(); - scrollBarAlphaFuture = 1f; - scrollAmount = target = MathHelper.clamp(scrollAmount + double_4 * double_6, 0, height - scrollListBounds.height + 2); - return true; - } + if (scrolling.mouseDragged(mouseX, mouseY, int_1, double_3, double_4)) { + scrollBarAlphaFutureTime = System.currentTimeMillis(); + scrollBarAlphaFuture = 1f; + return true; } return super.mouseDragged(mouseX, mouseY, int_1, double_3, double_4); } 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 index f348ce6a1..6588fa412 100644 --- a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java @@ -36,10 +36,10 @@ 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.api.REIHelper; import me.shedaniel.rei.api.widgets.Tooltip; import me.shedaniel.rei.gui.OverlaySearchField; import me.shedaniel.rei.gui.widget.EntryWidget; +import me.shedaniel.rei.gui.widget.ScrollingContainer; import me.shedaniel.rei.impl.ScreenHelper; import me.shedaniel.rei.impl.SearchArgument; import me.shedaniel.rei.utils.CollectionUtils; @@ -47,9 +47,6 @@ 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; @@ -66,10 +63,22 @@ import static me.shedaniel.rei.gui.widget.EntryListWidget.entrySize; @ApiStatus.Internal public class FilteringEntry extends AbstractConfigListEntry> { protected List selected = Lists.newArrayList(); - protected double target; - protected double scroll; - protected long start; - protected long duration; + protected final ScrollingContainer scrolling = new ScrollingContainer() { + @Override + public int getMaxScrollHeight() { + return MathHelper.ceil(entryStacks.size() / (innerBounds.width / (float) entrySize())) * entrySize() + 28; + } + + @Override + public Rectangle getBounds() { + return FilteringEntry.this.getBounds(); + } + + @Override + public int getScrollBarX() { + return getParent().right - 7; + } + }; private Consumer> saveConsumer; private List defaultValue; private List configFiltered; @@ -79,7 +88,6 @@ public class FilteringEntry extends AbstractConfigListEntry> { 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; @@ -118,7 +126,7 @@ public class FilteringEntry extends AbstractConfigListEntry> { 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); + entry.getBounds().y = (int) (entry.backupY - scrolling.scrollAmount); if (entry.isSelected() && !entry.isFiltered()) { configFiltered.add(stack); getScreen().setEdited(true, false); @@ -132,7 +140,7 @@ public class FilteringEntry extends AbstractConfigListEntry> { 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); + entry.getBounds().y = (int) (entry.backupY - scrolling.scrollAmount); if (entry.isSelected() && configFiltered.remove(stack)) { getScreen().setEdited(true, false); } @@ -192,13 +200,13 @@ public class FilteringEntry extends AbstractConfigListEntry> { return; for (EntryListEntry entry : entries) entry.clearStacks(); - int skip = Math.max(0, MathHelper.floor(scroll / (float) entrySize())); + int skip = Math.max(0, MathHelper.floor(scrolling.scrollAmount / (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); + entry.getBounds().y = (int) (entry.backupY - scrolling.scrollAmount); if (entry.getBounds().y > bounds.getMaxY()) break; entry.entry(stack); @@ -206,7 +214,7 @@ public class FilteringEntry extends AbstractConfigListEntry> { nextIndex++; } updatePosition(delta); - renderScrollbar(); + scrolling.renderScrollBar(0xff000000, 1); RenderSystem.translatef(0, 0, 300); this.searchField.laterRender(mouseX, mouseY, delta); this.selectAllButton.render(mouseX, mouseY, delta); @@ -224,95 +232,33 @@ public class FilteringEntry extends AbstractConfigListEntry> { Point p = secondPoint; if (p == null) { p = PointHelper.ofMouse(); - p.translate(0, (int) scroll); + p.translate(0, (int) scrolling.scrollAmount); } 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(left, (int) (top - scrolling.scrollAmount), 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); - } + public boolean mouseDragged(double mouseX, double mouseY, int button, double dx, double dy) { + if (scrolling.mouseDragged(mouseX, mouseY, button, dx, dy, true)) 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.ofMouse()); - float bottomC = (hovered ? .67f : .5f) * (REIHelper.getInstance().isDarkThemeEnabled() ? 0.8f : 1f); - float topC = (hovered ? .87f : .67f) * (REIHelper.getInstance().isDarkThemeEnabled() ? 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(); - } + return super.mouseDragged(mouseX, mouseY, button, dx, dy); } 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); + if (ConfigObject.getInstance().doesSnapToRows() && scrolling.scrollTarget >= 0 && scrolling.scrollTarget <= scrolling.getMaxScroll()) { + double nearestRow = Math.round(scrolling.scrollTarget / (double) entrySize()) * (double) entrySize(); + if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(scrolling.scrollTarget, nearestRow, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON)) + scrolling.scrollTarget += (nearestRow - scrolling.scrollTarget) * Math.min(delta / 2.0, 1.0); else - target = nearestRow; + scrolling.scrollTarget = 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]; + scrolling.updatePosition(delta); } public void updateSearch(String searchTerm) { @@ -362,19 +308,10 @@ public class FilteringEntry extends AbstractConfigListEntry> { @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 (scrolling.updateDraggingState(double_1, double_2, int_1)) + return true; - if (bounds.contains(double_1, double_2)) { + if (getBounds().contains(double_1, double_2)) { if (searchField.mouseClicked(double_1, double_2, int_1)) { this.selectionPoint = null; this.secondPoint = null; @@ -389,7 +326,7 @@ public class FilteringEntry extends AbstractConfigListEntry> { return true; } if (int_1 == 0) { - this.selectionPoint = new Point(double_1, double_2 + scroll); + this.selectionPoint = new Point(double_1, double_2 + scrolling.scrollAmount); this.secondPoint = null; return true; } @@ -400,7 +337,7 @@ public class FilteringEntry extends AbstractConfigListEntry> { @Override public boolean mouseReleased(double mouseX, double mouseY, int button) { if (selectionPoint != null && button == 0 && secondPoint == null) { - this.secondPoint = new Point(mouseX, mouseY + scroll); + this.secondPoint = new Point(mouseX, mouseY + scrolling.scrollAmount); if (secondPoint.equals(selectionPoint)) { secondPoint.translate(1, 1); } @@ -439,44 +376,10 @@ public class FilteringEntry extends AbstractConfigListEntry> { updateEntriesPosition(); } - 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); + scrolling.offset(ClothConfigInitializer.getScrollStep() * -double_3, true); return true; } super.mouseScrolled(double_1, double_2, double_3); diff --git a/src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenu.java b/src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenu.java new file mode 100644 index 000000000..676942350 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenu.java @@ -0,0 +1,256 @@ +package me.shedaniel.rei.gui.subsets; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import me.shedaniel.clothconfig2.ClothConfigInitializer; +import me.shedaniel.clothconfig2.api.ScissorsHandler; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.RoughlyEnoughItemsCore; +import me.shedaniel.rei.api.EntryRegistry; +import me.shedaniel.rei.api.EntryStack; +import me.shedaniel.rei.api.subsets.SubsetsRegistry; +import me.shedaniel.rei.gui.subsets.entries.EntryStackMenuEntry; +import me.shedaniel.rei.gui.subsets.entries.SubMenuEntry; +import me.shedaniel.rei.gui.widget.LateRenderable; +import me.shedaniel.rei.gui.widget.ScrollingContainer; +import me.shedaniel.rei.gui.widget.WidgetWithBounds; +import me.shedaniel.rei.impl.EntryRegistryImpl; +import me.shedaniel.rei.utils.CollectionUtils; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +@ApiStatus.Experimental +@ApiStatus.Internal +public class SubsetsMenu extends WidgetWithBounds implements LateRenderable { + public final Point menuStartPoint; + private final List entries = Lists.newArrayList(); + public final ScrollingContainer scrolling = new ScrollingContainer() { + @Override + public int getMaxScrollHeight() { + int i = 0; + for (SubsetsMenuEntry entry : children()) { + i += entry.getEntryHeight(); + } + return i; + } + + @Override + public Rectangle getBounds() { + return SubsetsMenu.this.getInnerBounds(); + } + + @Override + public boolean hasScrollBar() { + return SubsetsMenu.this.hasScrollBar(); + } + }; + + public SubsetsMenu(Point menuStartPoint, Collection entries) { + this.menuStartPoint = menuStartPoint; + buildEntries(entries); + } + + public static SubsetsMenu createFromRegistry(Point menuStartPoint) { + List stacks = EntryRegistry.getInstance().getStacksList(); + Map entries = Maps.newHashMap(); + { + // All Entries group + Map allEntries = getOrCreateSubEntryInMap(entries, "roughlyenoughitems:all_entries"); + for (EntryStack stack : stacks) { + putEntryInMap(allEntries, stack); + } + } + { + // Item Groups group + Map itemGroups = getOrCreateSubEntryInMap(entries, "roughlyenoughitems:item_groups"); + for (Item item : Registry.ITEM) { + ItemGroup group = item.getGroup(); + if (group == null) + continue; + DefaultedList list; + try { + list = new EntryRegistryImpl.DefaultedLinkedList<>(Lists.newLinkedList(), null); + item.appendStacks(group, list); + if (list.isEmpty()) + list.add(item.getStackForRender()); + Map groupMenu = getOrCreateSubEntryInMap(itemGroups, "_item_group_" + group.getId()); + for (ItemStack stack : list) { + putEntryInMap(groupMenu, EntryStack.create(stack)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + Set paths = SubsetsRegistry.INSTANCE.getPaths(); + for (String path : paths) { + Map lastMap = entries; + String[] pathSegments = path.split("/"); + for (String pathSegment : pathSegments) { + lastMap = getOrCreateSubEntryInMap(lastMap, pathSegment); + } + for (EntryStack entry : SubsetsRegistry.INSTANCE.getPathEntries(path)) { + EntryStack firstStack = CollectionUtils.findFirstOrNullEqualsEntryIgnoreAmount(stacks, entry); + if (firstStack != null) + putEntryInMap(lastMap, firstStack); + } + } + return new SubsetsMenu(menuStartPoint, buildEntries(entries)); + } + + private static Map getOrCreateSubEntryInMap(Map parent, String pathSegment) { + putEntryInMap(parent, pathSegment); + return (Map) parent.get(pathSegment); + } + + private static void putEntryInMap(Map parent, String pathSegment) { + if (!parent.containsKey(pathSegment)) { + parent.put(pathSegment, Maps.newHashMap()); + } + } + + private static void putEntryInMap(Map parent, EntryStack stack) { + Set items = (Set) parent.get("items"); + if (items == null) { + items = Sets.newLinkedHashSet(); + parent.put("items", items); + } + items.add(stack); + } + + private static List buildEntries(Map map) { + List entries = Lists.newArrayList(); + for (Map.Entry entry : map.entrySet()) { + if (entry.getKey().equals("items")) { + Set items = (Set) entry.getValue(); + for (EntryStack item : items) { + entries.add(new EntryStackMenuEntry(item)); + } + } else { + Map entryMap = (Map) entry.getValue(); + if (entry.getKey().startsWith("_item_group_")) { + entries.add(new SubMenuEntry(I18n.translate(entry.getKey().replace("_item_group_", "itemGroup.")), buildEntries(entryMap))); + } else { + String translationKey = "subsets.rei." + entry.getKey().replace(':', '.'); + if (!I18n.hasTranslation(translationKey)) + RoughlyEnoughItemsCore.LOGGER.warn("[REI] Subsets menu " + translationKey + " does not have a translation"); + entries.add(new SubMenuEntry(I18n.translate(translationKey), buildEntries(entryMap))); + } + } + } + return entries; + } + + @SuppressWarnings("deprecation") + private void buildEntries(Collection entries) { + this.entries.clear(); + this.entries.addAll(entries); + this.entries.sort(Comparator.comparing(entry -> entry instanceof SubMenuEntry ? 0 : 1).thenComparing(entry -> entry instanceof SubMenuEntry ? ((SubMenuEntry) entry).text : "")); + for (SubsetsMenuEntry entry : this.entries) { + entry.parent = this; + } + } + + @Override + public @NotNull Rectangle getBounds() { + return new Rectangle(menuStartPoint.x, menuStartPoint.y, getMaxEntryWidth() + 2 + (hasScrollBar() ? 6 : 0), getInnerHeight() + 2); + } + + public Rectangle getInnerBounds() { + return new Rectangle(menuStartPoint.x + 1, menuStartPoint.y + 1, getMaxEntryWidth() + (hasScrollBar() ? 6 : 0), getInnerHeight()); + } + + public boolean hasScrollBar() { + return scrolling.getMaxScrollHeight() > getInnerHeight(); + } + + public int getInnerHeight() { + return Math.min(scrolling.getMaxScrollHeight(), minecraft.currentScreen.height - 20 - menuStartPoint.y); + } + + public int getMaxEntryWidth() { + int i = 0; + for (SubsetsMenuEntry entry : children()) { + if (entry.getEntryWidth() > i) + i = entry.getEntryWidth(); + } + return Math.max(10, i); + } + + @Override + public void render(int mouseX, int mouseY, float delta) { + Rectangle bounds = getBounds(); + Rectangle innerBounds = getInnerBounds(); + fill(bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), -6250336); + fill(innerBounds.x, innerBounds.y, innerBounds.getMaxX(), innerBounds.getMaxY(), -16777216); + boolean contains = innerBounds.contains(mouseX, mouseY); + SubsetsMenuEntry focused = getFocused() instanceof SubsetsMenuEntry ? (SubsetsMenuEntry) getFocused() : null; + int currentY = (int) (innerBounds.y - scrolling.scrollAmount); + for (SubsetsMenuEntry child : children()) { + boolean containsMouse = contains && mouseY >= currentY && mouseY < currentY + child.getEntryHeight(); + if (containsMouse) { + focused = child; + } + currentY += child.getEntryHeight(); + } + currentY = (int) (innerBounds.y - scrolling.scrollAmount); + ScissorsHandler.INSTANCE.scissor(scrolling.getScissorBounds()); + for (SubsetsMenuEntry child : children()) { + boolean rendering = currentY + child.getEntryHeight() >= innerBounds.y && currentY <= innerBounds.getMaxY(); + boolean containsMouse = contains && mouseY >= currentY && mouseY < currentY + child.getEntryHeight(); + child.updateInformation(innerBounds.x, currentY, focused == child || containsMouse, containsMouse, rendering, getMaxEntryWidth()); + if (rendering) + child.render(mouseX, mouseY, delta); + currentY += child.getEntryHeight(); + } + ScissorsHandler.INSTANCE.removeLastScissor(); + setFocused(focused); + scrolling.renderScrollBar(); + scrolling.updatePosition(delta); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (scrolling.updateDraggingState(mouseX, mouseY, button)) + return true; + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (scrolling.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) + return true; + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (getInnerBounds().contains(mouseX, mouseY)) { + scrolling.offset(ClothConfigInitializer.getScrollStep() * -amount, true); + return true; + } + for (SubsetsMenuEntry child : children()) { + if (child instanceof SubMenuEntry) { + if (child.mouseScrolled(mouseX, mouseY, amount)) + return true; + } + } + return super.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public List children() { + return entries; + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenuEntry.java b/src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenuEntry.java new file mode 100644 index 000000000..5d84162b6 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/subsets/SubsetsMenuEntry.java @@ -0,0 +1,21 @@ +package me.shedaniel.rei.gui.subsets; + +import me.shedaniel.rei.gui.widget.Widget; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +@ApiStatus.Internal +public abstract class SubsetsMenuEntry extends Widget { + @Deprecated + SubsetsMenu parent = null; + + public final SubsetsMenu getParent() { + return parent; + } + + public abstract int getEntryWidth(); + + public abstract int getEntryHeight(); + + public abstract void updateInformation(int xPos, int yPos, boolean selected, boolean containsMouse, boolean rendering, int width); +} diff --git a/src/main/java/me/shedaniel/rei/gui/subsets/entries/EntryStackMenuEntry.java b/src/main/java/me/shedaniel/rei/gui/subsets/entries/EntryStackMenuEntry.java new file mode 100644 index 000000000..5b1920a02 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/subsets/entries/EntryStackMenuEntry.java @@ -0,0 +1,117 @@ +package me.shedaniel.rei.gui.subsets.entries; + +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.RoughlyEnoughItemsCore; +import me.shedaniel.rei.api.*; +import me.shedaniel.rei.gui.ContainerScreenOverlay; +import me.shedaniel.rei.gui.subsets.SubsetsMenu; +import me.shedaniel.rei.gui.subsets.SubsetsMenuEntry; +import me.shedaniel.rei.gui.widget.Widget; +import me.shedaniel.rei.impl.EntryRegistryImpl; +import me.shedaniel.rei.impl.ScreenHelper; +import me.shedaniel.rei.utils.CollectionUtils; +import net.minecraft.client.gui.Element; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.sound.SoundEvents; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Collections; +import java.util.List; + +@ApiStatus.Experimental +@ApiStatus.Internal +public class EntryStackMenuEntry extends SubsetsMenuEntry { + final EntryStack stack; + private int x, y, width; + private boolean selected, containsMouse, rendering; + private boolean clickedLast = false; + private Boolean isFiltered = null; + + public EntryStackMenuEntry(EntryStack stack) { + this.stack = stack; + } + + @Override + public int getEntryWidth() { + return 18; + } + + @Override + public int getEntryHeight() { + return 18; + } + + @Override + public void updateInformation(int xPos, int yPos, boolean selected, boolean containsMouse, boolean rendering, int width) { + this.x = xPos; + this.y = yPos; + this.selected = selected; + this.containsMouse = containsMouse; + this.rendering = rendering; + this.width = width; + } + + @Override + public void render(int mouseX, int mouseY, float delta) { + if (isFiltered()) { + if (selected) { + fill(x, y, x + width, y + 18, -26215); + } else { + fill(x, y, x + width, y + 18, -65536); + } + } else if (selected) { + fill(x, y, x + width, y + 18, 1174405119); + } + if (containsMouse && mouseX >= x + (width / 2) - 8 && mouseX <= x + (width / 2) + 8 && mouseY >= y + 1 && mouseY <= y + 17) { + REIHelper.getInstance().queueTooltip(stack.getTooltip(new Point(mouseX, mouseY))); + if (RoughlyEnoughItemsCore.isLeftModePressed && !clickedLast) { + clickedLast = true; + if (!getParent().scrolling.draggingScrollBar) { + minecraft.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + List filteredStacks = ConfigObject.getInstance().getFilteredStacks(); + if (isFiltered()) { + filteredStacks.removeIf(next -> next.equalsIgnoreAmount(stack)); + } else { + filteredStacks.add(stack.copy()); + } + SubsetsMenu subsetsMenu = ScreenHelper.getLastOverlay().getSubsetsMenu(); + if (subsetsMenu != null) + recalculateFilter(subsetsMenu); + ConfigManager.getInstance().saveConfig(); + ((EntryRegistryImpl) EntryRegistry.getInstance()).refilter(); + if (ScreenHelper.getSearchField() != null) + ContainerScreenOverlay.getEntryListWidget().updateSearch(ScreenHelper.getS