From 6d092d7599d27748abd8dc50b0c87e7fdba689cd Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 28 Apr 2022 14:03:31 +0800 Subject: REI 8.2 - Display History - Draggable Component - Multi Select Filtering Screen - Better Craftable Filter - Fix #850 - Fix #845 - Fix #832 - Fix #731 - Fix #839 - Fix #804 - Fix EvilCraft --- .../rei/impl/client/config/ConfigObjectImpl.java | 6 +- .../client/config/entries/FilteringScreen.java | 95 +++--- .../rei/impl/client/gui/RecipeDisplayExporter.java | 23 +- .../rei/impl/client/gui/ScreenOverlayImpl.java | 14 +- .../client/gui/dragging/CurrentDraggingStack.java | 99 ++++-- .../gui/screen/AbstractDisplayViewingScreen.java | 7 +- .../gui/screen/DefaultDisplayViewingScreen.java | 87 +++-- .../client/gui/widget/AutoCraftingEvaluator.java | 7 + .../client/gui/widget/DisplayCompositeWidget.java | 170 ++++++++++ .../rei/impl/client/gui/widget/EntryWidget.java | 10 +- .../impl/client/gui/widget/InternalWidgets.java | 61 ++-- .../rei/impl/client/gui/widget/MergedWidget.java | 45 +++ .../client/gui/widget/basewidgets/ArrowWidget.java | 4 +- .../gui/widget/basewidgets/BurningFireWidget.java | 4 +- .../client/gui/widget/basewidgets/PanelWidget.java | 4 +- .../gui/widget/entrylist/EntryListWidget.java | 22 +- .../gui/widget/favorites/FavoritesListWidget.java | 66 +++- .../gui/widget/favorites/history/DisplayEntry.java | 152 +++++++++ .../favorites/history/DisplayHistoryWidget.java | 365 +++++++++++++++++++++ .../panel/FadingFavoritesPanelButton.java | 2 +- .../gui/widget/favorites/panel/FavoritesPanel.java | 2 +- .../panel/rows/FavoritesPanelEntriesRow.java | 2 +- .../gui/widget/favorites/trash/TrashWidget.java | 41 ++- .../gui/widget/region/EntryStacksRegionWidget.java | 7 +- .../client/gui/widget/region/RegionListener.java | 5 + .../gui/widget/region/RegionRenderingDebugger.java | 3 + .../registry/category/CategoryRegistryImpl.java | 7 +- .../client/registry/screen/ScreenRegistryImpl.java | 58 +++- .../shedaniel/rei/impl/client/view/ViewsImpl.java | 11 + .../impl/common/entry/type/EntryRegistryImpl.java | 5 +- .../rei/impl/common/util/RectangleUtils.java | 52 +++ .../plugin/client/entry/ItemEntryDefinition.java | 17 +- .../client/runtime/DefaultClientRuntimePlugin.java | 12 +- 33 files changed, 1245 insertions(+), 220 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DisplayCompositeWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/history/DisplayEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/history/DisplayHistoryWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/common/util/RectangleUtils.java (limited to 'runtime/src/main/java/me') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index 1189436c2..28f45277b 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -167,11 +167,11 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @Override public boolean isCraftableFilterEnabled() { - return appearance.layout.enableCraftableOnlyButton; + return appearance.layout.showCraftableOnlyButton; } public void setCraftableFilterEnabled(boolean enabled) { - appearance.layout.enableCraftableOnlyButton = enabled; + appearance.layout.showCraftableOnlyButton = enabled; } @Override @@ -570,7 +570,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { private SearchFieldLocation searchFieldLocation = SearchFieldLocation.CENTER; @Comment("Declares the position of the config button.") @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) private ConfigButtonPosition configButtonLocation = ConfigButtonPosition.LOWER; - @Comment("Declares whether the craftable filter button is enabled.") private boolean enableCraftableOnlyButton = true; + @Comment("Declares whether the craftable filter button is enabled.") private boolean showCraftableOnlyButton = true; } @UsePercentage(min = 0.1, max = 1.0, prefix = "Limit: ") private double horizontalEntriesBoundaries = 1.0; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java index 83490fa16..68751f5d6 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java @@ -45,6 +45,7 @@ import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.widget.BatchedEntryRendererManager; import me.shedaniel.rei.impl.client.gui.widget.EntryWidget; import me.shedaniel.rei.impl.client.gui.widget.search.OverlaySearchField; +import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.events.GuiEventListener; @@ -57,9 +58,11 @@ import net.minecraft.util.Mth; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.function.Predicate; import static me.shedaniel.rei.impl.client.gui.widget.entrylist.EntryListWidget.entrySize; @@ -91,8 +94,9 @@ public class FilteringScreen extends Screen { private List entries = Collections.emptyList(); private List elements = Collections.emptyList(); - private Point selectionPoint = null; - private Point secondPoint = null; + private record PointPair(Point firstPoint, @Nullable Point secondPoint) {} + + private List points = new ArrayList<>(); private OverlaySearchField searchField; private Button selectAllButton; @@ -100,7 +104,7 @@ public class FilteringScreen extends Screen { private Button hideButton; private Button showButton; private Button backButton; - private Rectangle selectionCache; + private Predicate selectionCache; private SearchFilter lastFilter = SearchFilter.matchAll(); @@ -111,15 +115,14 @@ public class FilteringScreen extends Screen { { Component selectAllText = new TranslatableComponent("config.roughlyenoughitems.filteredEntries.selectAll"); this.selectAllButton = new Button(0, 0, Minecraft.getInstance().font.width(selectAllText) + 10, 20, selectAllText, button -> { - this.selectionPoint = new Point(-Integer.MAX_VALUE / 2, -Integer.MAX_VALUE / 2); - this.secondPoint = new Point(Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2); + this.points.clear(); + this.points.add(new PointPair(new Point(-Integer.MAX_VALUE / 2, -Integer.MAX_VALUE / 2), new Point(Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2))); }); } { Component selectNoneText = new TranslatableComponent("config.roughlyenoughitems.filteredEntries.selectNone"); this.selectNoneButton = new Button(0, 0, Minecraft.getInstance().font.width(selectNoneText) + 10, 20, selectNoneText, button -> { - this.selectionPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); - this.secondPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + this.points.clear(); }); } { @@ -268,27 +271,35 @@ public class FilteringScreen extends Screen { } this.font.drawShadow(matrices, this.title.getVisualOrderText(), this.width / 2.0F - this.font.width(this.title) / 2.0F, 12.0F, -1); + Component hint = new TranslatableComponent("config.roughlyenoughitems.filteringRulesScreen.hint").withStyle(ChatFormatting.YELLOW); + this.font.drawShadow(matrices, hint, this.width - this.font.width(hint) - 15, 12.0F, -1); } - private Rectangle getSelection() { + private Predicate getSelection() { return selectionCache; } private void updateSelectionCache() { - if (selectionPoint != null) { - Point p = secondPoint; - if (p == null) { - p = PointHelper.ofMouse(); - p.translate(0, scrolling.scrollAmountInt()); + if (!points.isEmpty()) { + Predicate predicate = rect -> false; + for (PointPair pair : points) { + Point firstPoint = pair.firstPoint(); + Point secondPoint = pair.secondPoint(); + if (secondPoint == null) { + secondPoint = PointHelper.ofMouse(); + secondPoint.translate(0, scrolling.scrollAmountInt()); + } + int left = Math.min(firstPoint.x, secondPoint.x); + int top = Math.min(firstPoint.y, secondPoint.y); + int right = Math.max(firstPoint.x, secondPoint.x); + int bottom = Math.max(firstPoint.y, secondPoint.y); + Rectangle rectangle = new Rectangle(left, top - scrolling.scrollAmountInt(), Math.max(1, right - left), Math.max(1, bottom - top)); + predicate = predicate.or(rectangle::intersects); } - 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); - selectionCache = new Rectangle(left, top - scrolling.scrollAmountInt(), right - left, bottom - top); + selectionCache = predicate; return; } - selectionCache = new Rectangle(0, 0, 0, 0); + selectionCache = rect -> false; } @Override @@ -349,41 +360,41 @@ public class FilteringScreen extends Screen { } @Override - public boolean mouseClicked(double double_1, double double_2, int int_1) { - if (scrolling.updateDraggingState(double_1, double_2, int_1)) + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (scrolling.updateDraggingState(mouseX, mouseY, button)) return true; - if (getBounds().contains(double_1, double_2)) { - if (searchField.mouseClicked(double_1, double_2, int_1)) { - this.selectionPoint = null; - this.secondPoint = null; + if (getBounds().contains(mouseX, mouseY)) { + if (searchField.mouseClicked(mouseX, mouseY, button)) { + this.points.clear(); return true; - } else if (selectAllButton.mouseClicked(double_1, double_2, int_1)) { + } else if (selectAllButton.mouseClicked(mouseX, mouseY, button)) { return true; - } else if (selectNoneButton.mouseClicked(double_1, double_2, int_1)) { + } else if (selectNoneButton.mouseClicked(mouseX, mouseY, button)) { return true; - } else if (hideButton.mouseClicked(double_1, double_2, int_1)) { + } else if (hideButton.mouseClicked(mouseX, mouseY, button)) { return true; - } else if (showButton.mouseClicked(double_1, double_2, int_1)) { + } else if (showButton.mouseClicked(mouseX, mouseY, button)) { return true; - } - if (int_1 == 0) { - this.selectionPoint = new Point(double_1, double_2 + scrolling.scrollAmount()); - this.secondPoint = null; + } else if (button == 0) { + if (!Screen.hasShiftDown()) { + this.points.clear(); + } + this.points.add(new PointPair(new Point(mouseX, mouseY + scrolling.scrollAmount()), null)); return true; } } - return backButton.mouseClicked(double_1, double_2, int_1); + return backButton.mouseClicked(mouseX, mouseY, button); } @Override public boolean mouseReleased(double mouseX, double mouseY, int button) { - if (selectionPoint != null && button == 0 && secondPoint == null) { - this.secondPoint = new Point(mouseX, mouseY + scrolling.scrollAmount()); - if (secondPoint.equals(selectionPoint)) { - secondPoint.translate(1, 1); + if (button == 0 && !points.isEmpty()) { + PointPair pair = this.points.get(points.size() - 1); + if (pair.secondPoint() == null) { + this.points.set(points.size() - 1, new PointPair(pair.firstPoint(), new Point(mouseX, mouseY + scrolling.scrollAmount()))); + return true; } - return true; } return super.mouseReleased(mouseX, mouseY, button); } @@ -402,8 +413,8 @@ public class FilteringScreen extends Screen { if (element.keyPressed(keyCode, scanCode, modifiers)) return true; if (Screen.isSelectAll(keyCode)) { - this.selectionPoint = new Point(0, 0); - this.secondPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + this.points.clear(); + this.points.add(new PointPair(new Point(-Integer.MAX_VALUE / 2, -Integer.MAX_VALUE / 2), new Point(Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2))); return true; } if (keyCode == 256 && this.shouldCloseOnEsc()) { @@ -469,7 +480,7 @@ public class FilteringScreen extends Screen { } public boolean isSelected() { - return getSelection().intersects(getBounds()); + return getSelection().test(getBounds()); } public boolean isFiltered() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/RecipeDisplayExporter.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/RecipeDisplayExporter.java index a81f51488..9895def4f 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/RecipeDisplayExporter.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/RecipeDisplayExporter.java @@ -34,15 +34,18 @@ import com.mojang.math.Matrix4f; import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.impl.client.gui.toast.ExportRecipeIdentifierToast; +import me.shedaniel.rei.impl.display.DisplaySpec; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.resources.language.I18n; +import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.ApiStatus; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; import java.util.List; @@ -53,13 +56,19 @@ public final class RecipeDisplayExporter extends Widget { private RecipeDisplayExporter() {} - public static void exportRecipeDisplay(Rectangle rectangle, List widgets) { - INSTANCE.exportRecipe(rectangle, widgets); - ExportRecipeIdentifierToast.addToast(I18n.get("msg.rei.exported_recipe"), I18n.get("msg.rei.exported_recipe.desc")); + public static void exportRecipeDisplay(Rectangle rectangle, DisplaySpec display, List widgets, boolean toast) { + INSTANCE.exportRecipe(rectangle, display, widgets); + if (toast) { + ExportRecipeIdentifierToast.addToast(I18n.get("msg.rei.exported_recipe"), I18n.get("msg.rei.exported_recipe.desc")); + } } - private static File getExportFilename(File directory) { + private static File getExportFilename(DisplaySpec display, File directory) { + Collection locations = display.provideInternalDisplayIds(); String string = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date()); + if (!locations.isEmpty()) { + string = locations.iterator().next().toString().replace('/', '_'); + } int i = 1; while (true) { @@ -72,7 +81,7 @@ public final class RecipeDisplayExporter extends Widget { } } - private void exportRecipe(Rectangle rectangle, List widgets) { + private void exportRecipe(Rectangle rectangle, DisplaySpec display, List widgets) { Minecraft client = Minecraft.getInstance(); Window window = client.getWindow(); RenderTarget renderTarget = new TextureTarget(window.getWidth(), window.getHeight(), true, false); @@ -105,9 +114,9 @@ public final class RecipeDisplayExporter extends Widget { } Util.ioPool().execute(() -> { try { - File export = new File(minecraft.gameDirectory, "rei_exports"); + File export = new File(minecraft.gameDirectory, "rei_exports/" + display.provideInternalDisplay().getCategoryIdentifier().toString().replace('/', '_')); export.mkdirs(); - strippedImage.writeToFile(getExportFilename(export)); + strippedImage.writeToFile(getExportFilename(display, export)); } catch (IOException e) { e.printStackTrace(); } finally { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java index b15bae714..3b5aaa3b1 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java @@ -40,9 +40,9 @@ import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.gui.config.DisplayPanelLocation; import me.shedaniel.rei.api.client.gui.config.SearchFieldLocation; import me.shedaniel.rei.api.client.gui.config.SyntaxHighlightingMode; -import me.shedaniel.rei.api.client.gui.drag.DraggableStackProvider; -import me.shedaniel.rei.api.client.gui.drag.DraggableStackVisitor; import me.shedaniel.rei.api.client.gui.drag.DraggingContext; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponentProvider; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponentVisitor; import me.shedaniel.rei.api.client.gui.screen.DisplayScreen; import me.shedaniel.rei.api.client.gui.widgets.Button; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; @@ -67,7 +67,9 @@ import me.shedaniel.rei.impl.client.gui.dragging.CurrentDraggingStack; import me.shedaniel.rei.impl.client.gui.modules.Menu; import me.shedaniel.rei.impl.client.gui.modules.MenuEntry; import me.shedaniel.rei.impl.client.gui.modules.entries.*; -import me.shedaniel.rei.impl.client.gui.widget.*; +import me.shedaniel.rei.impl.client.gui.widget.DefaultDisplayChoosePageWidget; +import me.shedaniel.rei.impl.client.gui.widget.InternalWidgets; +import me.shedaniel.rei.impl.client.gui.widget.LateRenderable; import me.shedaniel.rei.impl.client.gui.widget.entrylist.EntryListSearchManager; import me.shedaniel.rei.impl.client.gui.widget.entrylist.EntryListWidget; import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesListWidget; @@ -233,8 +235,8 @@ public class ScreenOverlayImpl extends ScreenOverlay { } public void init() { - draggingStack.set(DraggableStackProvider.from(() -> ScreenRegistry.getInstance().getDraggableProviders()), - DraggableStackVisitor.from(() -> ScreenRegistry.getInstance().getDraggableVisitors())); + draggingStack.set(DraggableComponentProvider.from(ScreenRegistry.getInstance()::getDraggableComponentProviders), + DraggableComponentVisitor.from(ScreenRegistry.getInstance()::getDraggableComponentVisitors)); this.shouldReload = false; this.shouldReloadSearch = false; @@ -413,7 +415,7 @@ public class ScreenOverlayImpl extends ScreenOverlay { } else if (ClientHelperImpl.getInstance().hasPermissionToUsePackets()) return new TranslatableComponent("text.rei.cheating_enabled"); else - return new TranslatableComponent("text.rei.cheating_limited_enabled"); + return new TranslatableComponent("text.rei.cheating_limited_enabled"); }), new SeparatorMenuEntry(), ToggleMenuEntry.ofDeciding(new TranslatableComponent("text.rei.config.menu.dark_theme"), diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/CurrentDraggingStack.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/CurrentDraggingStack.java index df2d8d452..c8b7d4546 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/CurrentDraggingStack.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/CurrentDraggingStack.java @@ -29,11 +29,17 @@ import me.shedaniel.clothconfig2.api.animator.ValueAnimator; import me.shedaniel.math.FloatingRectangle; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; -import me.shedaniel.math.impl.PointHelper; import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; -import me.shedaniel.rei.api.client.gui.drag.*; +import me.shedaniel.rei.api.client.gui.drag.DraggableBoundsProvider; +import me.shedaniel.rei.api.client.gui.drag.DraggableStack; +import me.shedaniel.rei.api.client.gui.drag.DraggedAcceptorResult; +import me.shedaniel.rei.api.client.gui.drag.DraggingContext; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponent; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponentProvider; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponentVisitor; import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.impl.client.gui.widget.LateRenderable; +import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; @@ -43,15 +49,16 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Supplier; +@SuppressWarnings("UnstableApiUsage") public class CurrentDraggingStack extends Widget implements LateRenderable, DraggingContext { - private DraggableStackProvider provider; - private DraggableStackVisitor visitor; + private DraggableComponentProvider provider; + private DraggableComponentVisitor visitor; @Nullable private DraggableEntry entry; private final List backToOriginals = new ArrayList<>(); private final Set bounds = new HashSet<>(); - public void set(DraggableStackProvider provider, DraggableStackVisitor visitor) { + public void set(DraggableComponentProvider provider, DraggableComponentVisitor visitor) { this.provider = provider; this.visitor = visitor; } @@ -69,16 +76,19 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag if (xDistance * xDistance + yDistance * yDistance > requiredDistance * requiredDistance) { entry.dragging = true; - entry.stack.drag(); + entry.startDragging = Util.getMillis(); + entry.component.drag(); } } - if (!RoughlyEnoughItemsCoreClient.isLeftMousePressed) { - drop(); - } else if (entry.dragging) { + if (entry.dragging) { matrices.pushPose(); matrices.translate(0, 0, 600); - entry.stack.render(matrices, new Rectangle(mouseX - 8, mouseY - 8, 16, 16), mouseX, mouseY, delta); + entry.bounds.update(delta); + int width = entry.component.getWidth(); + int height = entry.component.getHeight(); + entry.bounds.setTo(new FloatingRectangle(mouseX - width / 2, mouseY - height / 2, width, height), Util.getMillis() - entry.startDragging < 100 && width * height > 1000 ? 80 : 10); + entry.component.render(matrices, entry.bounds.value().getBounds(), mouseX, mouseY, delta); matrices.popPose(); VoxelShape shape = entry.getBoundsProvider().bounds(); @@ -87,6 +97,10 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag bounds.add(shapeBounds); hash = shapeBounds.hash; } + + if (!RoughlyEnoughItemsCoreClient.isLeftMousePressed) { + drop(); + } } for (ShapeBounds bound : bounds) { @@ -119,12 +133,12 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag renderBackEntry.update(delta); FloatingRectangle value = renderBackEntry.bounds.value(); FloatingRectangle target = renderBackEntry.bounds.target(); - if (value.width < 2 || value.height < 2 || (Math.abs(value.x - target.x) <= 2 && Math.abs(value.y - target.y) <= 2 && Math.abs(value.width - target.width) <= 1 && Math.abs(value.height - target.height) <= 1)) { + if (value.width < 2 || value.height < 2 || (Math.abs(value.x - target.x) <= 1.3 && Math.abs(value.y - target.y) <= 1.3 && Math.abs(value.width - target.width) <= 1 && Math.abs(value.height - target.height) <= 1)) { iterator.remove(); } else { matrices.pushPose(); matrices.translate(0, 0, 600); - renderBackEntry.stack.render(matrices, value.getBounds(), mouseX, mouseY, delta); + renderBackEntry.component.render(matrices, value.getBounds(), mouseX, mouseY, delta); matrices.popPose(); } } @@ -138,7 +152,7 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { drop(); - DraggableStack hoveredStack = provider.getHoveredStack(this, mouseX, mouseY); + DraggableComponent hoveredStack = provider.getHovered(this, mouseX, mouseY); if (hoveredStack != null) { entry = new DraggableEntry(hoveredStack, new Point(mouseX, mouseY)); } @@ -157,8 +171,8 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag private boolean drop() { if (entry != null && entry.dragging) { - DraggedAcceptorResult result = visitor.acceptDraggedStack(this, entry.stack); - entry.stack.release(result); + DraggedAcceptorResult result = visitor.acceptDragged(this, entry.component); + entry.component.release(result); entry = null; return true; } @@ -175,42 +189,65 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag @Override @Nullable public DraggableStack getCurrentStack() { - return entry != null && entry.dragging ? entry.stack : null; + DraggableComponent dragged = getDragged(); + return dragged instanceof DraggableStack ? (DraggableStack) dragged : null; + } + + @Override + @Nullable + public DraggableComponent getDragged() { + return entry != null && entry.dragging ? entry.component : null; } @Override @Nullable public Point getCurrentPosition() { - return isDraggingStack() ? PointHelper.ofMouse() : null; + if (!isDraggingComponent()) return null; + FloatingRectangle rectangle = entry.bounds.value(); + return new Point(rectangle.getCenterX(), rectangle.getCenterY()); + } + + @Override + @Nullable + public Rectangle getCurrentBounds() { + if (!isDraggingComponent()) return null; + FloatingRectangle rectangle = entry.bounds.value(); + return rectangle.getBounds(); } @Override - public void renderBackToPosition(DraggableStack stack, Point initialPosition, Supplier position) { - backToOriginals.add(new RenderBackEntry(stack, new Rectangle(initialPosition.x - 8, initialPosition.y - 8, 16, 16), () -> { + public void renderBack(DraggableComponent component, Point initialPosition, Supplier position) { + int width = component.getWidth(); + int height = component.getHeight(); + backToOriginals.add(new RenderBackEntry(component, new Rectangle(initialPosition.x - width / 2, initialPosition.y - height / 2, width, height), () -> { Point point = position.get(); - return new Rectangle(point.x, point.y, 16, 16); + return new Rectangle(point.x, point.y, width, height); })); } @Override - public void renderBackToPosition(DraggableStack stack, Rectangle initialPosition, Supplier bounds) { - backToOriginals.add(new RenderBackEntry(stack, initialPosition, bounds)); + public void renderBack(DraggableComponent component, Rectangle initialPosition, Supplier bounds) { + backToOriginals.add(new RenderBackEntry(component, initialPosition, bounds)); } private class DraggableEntry { - private final DraggableStack stack; + private final DraggableComponent component; private final Point start; + private long startDragging = -1; + private final ValueAnimator bounds; private boolean dragging = false; - private DraggableStackVisitor.BoundsProvider boundsProvider; + private DraggableBoundsProvider boundsProvider; - private DraggableEntry(DraggableStack stack, Point start) { - this.stack = stack; + private DraggableEntry(DraggableComponent component, Point start) { + this.component = component; this.start = start; + this.bounds = ValueAnimator.ofFloatingRectangle() + .setAs(component.getOriginBounds(start).getFloatingBounds()); } - public DraggableStackVisitor.BoundsProvider getBoundsProvider() { + public DraggableBoundsProvider getBoundsProvider() { if (boundsProvider == null) { - boundsProvider = DraggableStackVisitor.BoundsProvider.concat(visitor.getDraggableAcceptingBounds(CurrentDraggingStack.this, stack).toList()); + boundsProvider = DraggableBoundsProvider.concat(visitor.getDraggableAcceptingBounds(CurrentDraggingStack.this, component).toList()); } return boundsProvider; @@ -246,13 +283,13 @@ public class CurrentDraggingStack extends Widget implements LateRenderable, Drag } private static class RenderBackEntry { - private final DraggableStack stack; + private final DraggableComponent component; private final Supplier position; private ValueAnimator bounds = ValueAnimator.ofFloatingRectangle(); private int lastDestination = -1; - public RenderBackEntry(DraggableStack stack, Rectangle initialPosition, Supplier position) { - this.stack = stack; + public RenderBackEntry(DraggableComponent component, Rectangle initialPosition, Supplier position) { + this.component = component; this.bounds.setAs(new FloatingRectangle(initialPosition)); this.position = position; } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java index 842579570..212152aae 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java @@ -121,7 +121,7 @@ public abstract class AbstractDisplayViewingScreen extends Screen implements Dis } protected DisplayCategoryView getCurrentCategoryView(Display display) { - return CategoryRegistry.getInstance().get(categories.get(selectedCategoryIndex).getCategoryIdentifier().cast()) + return CategoryRegistry.getInstance().get(display.getCategoryIdentifier().cast()) .getView(display); } @@ -203,9 +203,8 @@ public abstract class AbstractDisplayViewingScreen extends Screen implements Dis collection = Registry.FLUID.getTagNames(); objects = CollectionUtils.map(widget.getEntries(), stack -> stack.castValue().getFluid().builtInRegistryHolder()); } else continue; - TagKey firstOrNull = collection.filter(key -> - CollectionUtils.anyMatch(objects, holder -> ((Holder) holder).is((TagKey) key))) - .findAny().orElse(null); + TagKey firstOrNull = CollectionUtils.findFirstOrNull(collection::iterator, + key -> CollectionUtils.allMatch(objects, holder -> ((Holder) holder).is((TagKey) key))); if (firstOrNull != null) { widget.tagMatch = firstOrNull.location(); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java index a5ea0a957..c2f49fb6a 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/DefaultDisplayViewingScreen.java @@ -23,12 +23,14 @@ package me.shedaniel.rei.impl.client.gui.screen; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.math.Matrix4f; +import it.unimi.dsi.fastutil.Pair; import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.clothconfig2.api.animator.ValueAnimator; import me.shedaniel.math.Color; @@ -54,18 +56,18 @@ import me.shedaniel.rei.impl.client.ClientHelperImpl; import me.shedaniel.rei.impl.client.REIRuntimeImpl; import me.shedaniel.rei.impl.client.gui.RecipeDisplayExporter; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; -import me.shedaniel.rei.impl.client.gui.widget.DefaultDisplayChoosePageWidget; -import me.shedaniel.rei.impl.client.gui.widget.EntryWidget; -import me.shedaniel.rei.impl.client.gui.widget.InternalWidgets; -import me.shedaniel.rei.impl.client.gui.widget.TabWidget; +import me.shedaniel.rei.impl.client.gui.toast.ExportRecipeIdentifierToast; +import me.shedaniel.rei.impl.client.gui.widget.*; import me.shedaniel.rei.impl.client.gui.widget.basewidgets.PanelWidget; import me.shedaniel.rei.impl.display.DisplaySpec; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.chat.NarratorChatListener; import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.ConfirmScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.resources.language.I18n; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; @@ -84,7 +86,7 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { public static final ResourceLocation CHEST_GUI_TEXTURE = new ResourceLocation("roughlyenoughitems", "textures/gui/recipecontainer.png"); private final List preWidgets = Lists.newArrayList(); private final List widgets = Lists.newArrayList(); - private final Map> recipeBounds = Maps.newHashMap(); + private final Map>> recipeBounds = Maps.newHashMap(); private final List tabs = Lists.newArrayList(); public int page; public int categoryPages = -1; @@ -154,7 +156,7 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { int maxHeight = Math.min(largestHeight, CollectionUtils., Integer>mapAndMax(categories, category -> (category.getDisplayHeight() + 4) * Math.max(1, Math.min(getRecipesPerPage(largestHeight, category) + 1, Math.max(categoryMap.get(category).size(), ConfigObject.getInstance().getMaxRecipePerPage()))) + 36, Comparator.naturalOrder()).orElse(66)); int totalDisplayHeight = (getCurrentCategory().getDisplayHeight() + 4) * Math.max(1, getRecipesPerPage(maxHeight, getCurrentCategory()) + 1) + 36; - int guiWidth = Math.max(maxWidthDisplay + 10, 190); + int guiWidth = Math.max(maxWidthDisplay + 10 + 14 + 14, 190); this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - maxHeight / 2, guiWidth, maxHeight); if (ConfigObject.getInstance().isSubsetsEnabled()) { this.bounds.setLocation(this.bounds.getX(), this.bounds.getY() + 15); @@ -277,9 +279,9 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { for (EntryWidget widget : Widgets.walk(widgets, EntryWidget.class::isInstance)) { widget.removeTagMatch = true; } - this.recipeBounds.put(displayBounds, setupDisplay); - this.widgets.addAll(setupDisplay); - if (plusButtonArea.isPresent() && plusButtonArea.get().get(displayBounds) != null) { + this.recipeBounds.put(displayBounds, Pair.of(display, setupDisplay)); + this.widgets.add(new DisplayCompositeWidget(display, setupDisplay, displayBounds)); + if (plusButtonArea.isPresent()) { this.widgets.add(InternalWidgets.createAutoCraftingButtonWidget(displayBounds, plusButtonArea.get().get(displayBounds), new TextComponent(plusButtonArea.get().getButtonText()), displaySupplier, display::provideInternalDisplayIds, setupDisplay, getCurrentCategory())); } } @@ -377,8 +379,7 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { { ModifierKeyCode export = ConfigObject.getInstance().getExportImageKeybind(); if (export.matchesCurrentKey() || export.matchesCurrentMouse()) { - for (Map.Entry> entry : recipeBounds.entrySet()) { - Rectangle bounds = entry.getKey(); + for (Rectangle bounds : Iterables.concat(recipeBounds.keySet(), Iterables.transform(tabs, TabWidget::getBounds))) { setBlitOffset(470); if (bounds.contains(mouseX, mouseY)) { fillGradient(matrices, bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), 1744822402, 1744822402); @@ -403,13 +404,7 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { public boolean keyReleased(int keyCode, int scanCode, int modifiers) { ModifierKeyCode export = ConfigObject.getInstance().getExportImageKeybind(); if (export.matchesKey(keyCode, scanCode)) { - for (Map.Entry> entry : recipeBounds.entrySet()) { - Rectangle bounds = entry.getKey(); - if (bounds.contains(PointHelper.ofMouse())) { - RecipeDisplayExporter.exportRecipeDisplay(bounds, entry.getValue()); - break; - } - } + if (checkExportDisplays()) return true; } return super.keyReleased(keyCode, scanCode, modifiers); } @@ -442,13 +437,7 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { public boolean mouseReleased(double mouseX, double mouseY, int button) { ModifierKeyCode export = ConfigObject.getInstance().getExportImageKeybind(); if (export.matchesMouse(button)) { - for (Map.Entry> entry : recipeBounds.entrySet()) { - Rectangle bounds = entry.getKey(); - if (bounds.contains(PointHelper.ofMouse())) { - RecipeDisplayExporter.exportRecipeDisplay(bounds, entry.getValue()); - break; - } - } + if (checkExportDisplays()) return true; } for (GuiEventListener entry : children()) if (entry.mouseReleased(mouseX, mouseY, button)) @@ -456,6 +445,54 @@ public class DefaultDisplayViewingScreen extends AbstractDisplayViewingScreen { return super.mouseReleased(mouseX, mouseY, button); } + private boolean checkExportDisplays() { + for (Map.Entry>> entry : recipeBounds.entrySet()) { + Rectangle bounds = entry.getKey(); + if (bounds.contains(PointHelper.ofMouse())) { + RecipeDisplayExporter.exportRecipeDisplay(bounds, entry.getValue().left(), entry.getValue().right(), true); + return true; + } + } + for (TabWidget tab : tabs) { + Rectangle bounds = tab.getBounds(); + if (bounds.contains(PointHelper.ofMouse())) { + minecraft.setScreen(new ConfirmScreen(confirmed -> { + if (confirmed) { + for (DisplaySpec spec : categoryMap.getOrDefault(tab.category, Collections.emptyList())) { + Display display = spec.provideInternalDisplay(); + int displayWidth = getCurrentCategory().getDisplayWidth(display); + int displayHeight = getCurrentCategory().getDisplayHeight(); + final Rectangle displayBounds = new Rectangle(0, 0, displayWidth, displayHeight); + List setupDisplay; + try { + setupDisplay = getCurrentCategoryView(display).setupDisplay(display, displayBounds); + } catch (Throwable throwable) { + throwable.printStackTrace(); + setupDisplay = new ArrayList<>(); + setupDisplay.add(Widgets.createRecipeBase(displayBounds).color(0xFFBB0000)); + setupDisplay.add(Widgets.createLabel(new Point(displayBounds.getCenterX(), displayBounds.getCenterY() - 8), new TextComponent("Failed to initiate setupDisplay"))); + setupDisplay.add(Widgets.createLabel(new Point(displayBounds.getCenterX(), displayBounds.getCenterY() + 1), new TextComponent("Check console for error"))); + } + setupTags(setupDisplay); + transformFiltering(setupDisplay); + transformIngredientNotice(setupDisplay, ingredientStackToNotice); + transformResultNotice(setupDisplay, resultStackToNotice); + for (EntryWidget widget : Widgets.walk(widgets, EntryWidget.class::isInstance)) { + widget.removeTagMatch = true; + } + + RecipeDisplayExporter.exportRecipeDisplay(displayBounds, spec, setupDisplay, false); + } + ExportRecipeIdentifierToast.addToast(I18n.get("msg.rei.exported_recipe"), I18n.get("msg.rei.exported_recipe.desc")); + } + minecraft.setScreen(DefaultDisplayViewingScreen.this); + }, new TranslatableComponent("text.rei.ask_to_export", tab.categoryName), + new TranslatableComponent("text.rei.ask_to_export.subtitle", categoryMap.getOrDefault(tab.category, Collections.emptyList()).size()))); + } + } + return false; + } + @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount) { REIRuntimeImpl.isWithinRecipeViewingScreen = true; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java index 8be294eb3..62c297dc9 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java @@ -25,6 +25,7 @@ package me.shedaniel.rei.impl.client.gui.widget; import me.shedaniel.math.Point; import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; import me.shedaniel.rei.api.client.overlay.ScreenOverlay; import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; @@ -45,6 +46,7 @@ import net.minecraft.resources.ResourceLocation; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; @@ -66,6 +68,11 @@ public class AutoCraftingEvaluator { result.tooltipRenderer = (pos, sink) -> { List str = new ArrayList<>(errorTooltip); + if (ConfigObject.getInstance().isFavoritesEnabled()) { + str.add(new TextComponent(" ")); + str.add(new TranslatableComponent("text.rei.save.recipes", new TextComponent(ConfigObject.getInstance().getFavoriteKeyCode().getLocalizedName().getString().toUpperCase(Locale.ROOT)).withStyle(ChatFormatting.BOLD)).withStyle(ChatFormatting.GRAY)); + } + if (Minecraft.getInstance().options.advancedItemTooltips && idsSupplier != null) { Collection locations = idsSupplier.get(); if (!locations.isEmpty()) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DisplayCompositeWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DisplayCompositeWidget.java new file mode 100644 index 000000000..1a02fbae3 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/DisplayCompositeWidget.java @@ -0,0 +1,170 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.gui.widget; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.ModifierKeyCode; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.math.impl.PointHelper; +import me.shedaniel.rei.api.client.config.ConfigManager; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.favorites.FavoriteEntry; +import me.shedaniel.rei.api.client.gui.drag.DraggedAcceptorResult; +import me.shedaniel.rei.api.client.gui.drag.DraggingContext; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponent; +import me.shedaniel.rei.api.client.gui.drag.component.DraggableComponentProviderWidget; +import me.shedaniel.rei.api.client.gui.widgets.DelegateWidgetWithBounds; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.api.client.overlay.ScreenOverlay; +import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry; +import me.shedaniel.rei.api.client.view.ViewSearchBuilder; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.impl.client.REIRuntimeImpl; +import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; +import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesListWidget; +import me.shedaniel.rei.impl.display.DisplaySpec; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.stream.StreamSupport; + +public class DisplayCompositeWidget extends DelegateWidgetWithBounds implements DraggableComponentProviderWidget { + private final DisplaySpec display; + + public DisplayCompositeWidget(DisplaySpec display, List widgets, Rectangle bounds) { + super(Widgets.concat(widgets), () -> bounds); + this.display = display; + } + + @Override + @Nullable + public DraggableComponent getHovered(DraggingContext context, double mouseX, double mouseY) { + return StreamSupport.stream(Widgets.>walk(widget.children(), widget -> widget instanceof DraggableComponentProviderWidget).spliterator(), false) + .map(widget -> widget.getHovered(context, mouseX, mouseY)) + .filter(Objects::nonNull) + .findFirst() + .orElseGet(() -> { + if (containsMouse(mouseX, mouseY)) { + return (DraggableComponent) (DraggableComponent) new DisplayDraggableComponent(widget, display.provideInternalDisplay(), getBounds(), getBounds()); + } else { + return null; + } + }); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (super.keyPressed(keyCode, scanCode, modifiers)) { + return true; + } + + if (ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.ofMouse())) { + if (ConfigObject.getInstance().getFavoriteKeyCode().matchesKey(keyCode, scanCode)) { + FavoritesListWidget favoritesListWidget = ScreenOverlayImpl.getFavoritesListWidget(); + + if (favoritesListWidget != null) { + favoritesListWidget.displayHistory.addDisplay(getBounds().clone(), display.provideInternalDisplay()); + return true; + } + } + } + + return false; + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (super.mouseReleased(mouseX, mouseY, button)) { + return true; + } + + if (ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(mouseX, mouseY)) { + if (ConfigObject.getInstance().getFavoriteKeyCode().matchesMouse(button)) { + FavoritesListWidget favoritesListWidget = ScreenOverlayImpl.getFavoritesListWidget(); + + if (favoritesListWidget != null) { + favoritesListWidget.displayHistory.addDisplay(getBounds().clone(), display.provideInternalDisplay()); + return true; + } + } + } + + return false; + } + + public static class DisplayDraggableComponent implements DraggableComponent { + private final Widget widget; + private final Display display; + private final Rectangle originBounds; + private final Rectangle bounds; + + public DisplayDraggableComponent(Widget widget, Display display, Rectangle originBounds, Rectangle bounds) { + this.widget = widget; + this.display = display; + this.originBounds = originBounds; + this.bounds = bounds; + } + + @Override + public int getWidth() { + return bounds.width; + } + + @Override + public int getHeight() { + return bounds.height; + } + + @Override + public Display get() { + return display; + } + + @Override + public void render(PoseStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) { + matrices.pushPose(); + matrices.translate(bounds.getX(), bounds.getY(), 0); + matrices.scale(bounds.width / (float) this.bounds.getWidth(), bounds.height / (float) this.bounds.getHeight(), 1); + matrices.translate(-this.bounds.getX(), -this.bounds.getY(), 0); + widget.render(matrices, -1000, -1000, delta); + matrices.popPose(); + } + + @Override + public void release(DraggedAcceptorResult result) { + if (result == DraggedAcceptorResult.PASS) { + DraggingContext.getInstance().renderBack(this, DraggingContext.getInstance().getCurrentBounds(), () -> originBounds); + } + } + + @Override + public Rectangle getOriginBounds(Point mouse) { + return originBounds.clone(); + } + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java index 30764dbdc..db4fb921c 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java @@ -314,10 +314,12 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { RenderSystem.setShaderTexture(0, RECIPE_GUI); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); blit(matrices, bounds.x, bounds.y, 0, 222, bounds.width, bounds.height); - RenderSystem.setShaderTexture(0, RECIPE_GUI_DARK); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, darkBackgroundAlpha.value()); - blit(matrices, bounds.x, bounds.y, 0, 222, bounds.width, bounds.height); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + if (darkBackgroundAlpha.value() > 0.0F) { + RenderSystem.setShaderTexture(0, RECIPE_GUI_DARK); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, darkBackgroundAlpha.value()); + blit(matrices, bounds.x, bounds.y, 0, 222, bounds.width, bounds.height); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + } } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java index f757bf9ad..dc87a3ff9 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java @@ -35,8 +35,10 @@ import me.shedaniel.rei.api.client.gui.widgets.*; import me.shedaniel.rei.api.client.registry.display.DisplayCategory; import me.shedaniel.rei.api.common.display.Display; import me.shedaniel.rei.impl.ClientInternals; +import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.toast.CopyRecipeIdentifierToast; import me.shedaniel.rei.impl.client.gui.widget.basewidgets.*; +import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesListWidget; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; @@ -45,6 +47,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.resources.language.I18n; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.ApiStatus; @@ -65,30 +68,30 @@ public final class InternalWidgets { AutoCraftingEvaluator.evaluateAutoCrafting(true, Screen.hasShiftDown(), displaySupplier.get(), idsSupplier); }); return new DelegateWidget(autoCraftingButton) { - boolean didJustRender = false; - @Override public void render(PoseStack poses, int mouseX, int mouseY, float delta) { - didJustRender = false; - AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, displaySupplier.get(), idsSupplier); autoCraftingButton.setEnabled(result.successful); autoCraftingButton.setTint(result.tint); + if (result.hasApplicable) { + autoCraftingButton.setText(text); + } else { + autoCraftingButton.setText(new TextComponent("?")); + } + if (result.hasApplicable && (containsMouse(mouseX, mouseY) || autoCraftingButton.isFocused()) && result.renderer != null) { result.renderer.render(poses, mouseX, mouseY, delta, setupDisplay, displayBounds, displaySupplier.get()); } - renderIf(result.hasApplicable, poses, mouseX, mouseY, delta); + this.widget.render(poses, mouseX, mouseY, delta); - if (didJustRender) { - if (!autoCraftingButton.isFocused() && containsMouse(mouseX, mouseY)) { - tryTooltip(result, new Point(mouseX, mouseY)); - } else if (autoCraftingButton.isFocused()) { - Rectangle bounds = autoCraftingButton.getBounds(); - tryTooltip(result, new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2)); - } + if (!autoCraftingButton.isFocused() && containsMouse(mouseX, mouseY)) { + tryTooltip(result, new Point(mouseX, mouseY)); + } else if (autoCraftingButton.isFocused()) { + Rectangle bounds = autoCraftingButton.getBounds(); + tryTooltip(result, new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2)); } } @@ -98,37 +101,47 @@ public final class InternalWidgets { } } - private void renderIf(boolean should, PoseStack poseStack, int mouseX, int mouseY, float delta) { - if (should) { - didJustRender = true; - this.widget.render(poseStack, mouseX, mouseY, delta); - } else if (Minecraft.getInstance().options.advancedItemTooltips) { - didJustRender = true; - this.widget.render(poseStack, mouseX, mouseY, delta); - } - } - @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (didJustRender && displaySupplier.get().getDisplayLocation().isPresent() && ConfigObject.getInstance().getCopyRecipeIdentifierKeybind().matchesKey(keyCode, scanCode) && containsMouse(PointHelper.ofMouse())) { + if (displaySupplier.get().getDisplayLocation().isPresent() && ConfigObject.getInstance().getCopyRecipeIdentifierKeybind().matchesKey(keyCode, scanCode) && containsMouse(PointHelper.ofMouse())) { minecraft.keyboardHandler.setClipboard(displaySupplier.get().getDisplayLocation().get().toString()); if (ConfigObject.getInstance().isToastDisplayedOnCopyIdentifier()) { CopyRecipeIdentifierToast.addToast(I18n.get("msg.rei.copied_recipe_id"), I18n.get("msg.rei.recipe_id_details", displaySupplier.get().getDisplayLocation().get().toString())); } return true; + } else if (ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.ofMouse())) { + if (ConfigObject.getInstance().getFavoriteKeyCode().matchesKey(keyCode, scanCode)) { + FavoritesListWidget favoritesListWidget = ScreenOverlayImpl.getFavoritesListWidget(); + + if (favoritesListWidget != null) { + favoritesListWidget.displayHistory.addDisplay(displayBounds.clone(), displaySupplier.get()); + return true; + } + } } + return super.keyPressed(keyCode, scanCode, modifiers); } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (didJustRender && displaySupplier.get().getDisplayLocation().isPresent() && ConfigObject.getInstance().getCopyRecipeIdentifierKeybind().matchesMouse(button) && containsMouse(PointHelper.ofMouse())) { + if (displaySupplier.get().getDisplayLocation().isPresent() && ConfigObject.getInstance().getCopyRecipeIdentifierKeybind().matchesMouse(button) && containsMouse(PointHelper.ofMouse())) { minecraft.keyboardHandler.setClipboard(displaySupplier.get().getDisplayLocation().get().toString()); if (ConfigObject.getInstance().isToastDisplayedOnCopyIdentifier()) { CopyRecipeIdentifierToast.addToast(I18n.get("msg.rei.copied_recipe_id"), I18n.get("msg.rei.recipe_id_details", displaySupplier.get().getDisplayLocation().get().toString())); } return true; + } else if (ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.ofMouse())) { + if (ConfigObject.getInstance().getFavoriteKeyCode().matchesMouse(button)) { + FavoritesListWidget favoritesListWidget = ScreenOverlayImpl.getFavoritesListWidget(); + + if (favoritesListWidget != null) { + favoritesListWidget.displayHistory.addDisplay(displayBounds.clone(), displaySupplier.get()); + return true; + } + } } + return super.mouseClicked(mouseX, mouseY, button); } }; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidget.java index e419a7db7..ecb5bc287 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/MergedWidget.java +++ b/runtime/sr