From 94e323f75c17e297c33fba1d3afb5c47ae66a8ad Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 28 Aug 2022 18:50:24 +0900 Subject: Separate EntryListWidget and FavoritesListWidget more --- .../client/gui/dragging/DraggingContextImpl.java | 311 +++++++++++++++++++ .../impl/client/gui/menu/AbstractMenuEntry.java | 77 +++++ .../shedaniel/rei/impl/client/gui/menu/Menu.java | 209 +++++++++++++ .../rei/impl/client/gui/menu/MenuAccess.java | 64 ++++ .../rei/impl/client/gui/menu/MenuAccessImpl.java | 134 +++++++++ .../rei/impl/client/gui/menu/MenuEntry.java | 44 +++ .../client/gui/menu/entries/EmptyMenuEntry.java | 58 ++++ .../gui/menu/entries/SeparatorMenuEntry.java | 53 ++++ .../impl/client/gui/menu/entries/SubMenuEntry.java | 153 ++++++++++ .../client/gui/menu/entries/TextMenuEntry.java | 70 +++++ .../client/gui/menu/entries/ToggleMenuEntry.java | 143 +++++++++ .../client/gui/overlay/AbstractScreenOverlay.java | 330 +++++++++++++++++++++ .../impl/client/gui/overlay/EntryListProvider.java | 9 - .../client/gui/overlay/FavoritesListProvider.java | 11 - .../client/gui/overlay/InternalOverlayBounds.java | 56 ++++ .../impl/client/gui/overlay/LateRenderable.java | 30 ++ .../impl/client/gui/overlay/ScreenOverlayImpl.java | 231 +++++++++++++++ .../gui/overlay/entries/EntryListProvider.java | 33 +++ .../gui/overlay/entries/EntryListWidget.java | 44 +++ .../gui/overlay/entries/FavoritesListProvider.java | 34 +++ .../gui/overlay/entries/FavoritesListWidget.java | 36 +++ .../overlay/widgets/DisplayTooltipComponent.java | 82 +++++ .../gui/overlay/widgets/DisplayedEntryWidget.java | 236 +++++++++++++++ .../gui/overlay/widgets/OverlayWidgetProvider.java | 37 +++ .../overlay/widgets/RegionRenderingDebugger.java | 80 +++++ .../gui/overlay/widgets/ScaleIndicatorWidget.java | 72 +++++ 26 files changed, 2617 insertions(+), 20 deletions(-) create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/DraggingContextImpl.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/AbstractMenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/Menu.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccess.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccessImpl.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/EmptyMenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SeparatorMenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SubMenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/TextMenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/ToggleMenuEntry.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/AbstractScreenOverlay.java delete mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/EntryListProvider.java delete mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/FavoritesListProvider.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/InternalOverlayBounds.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/LateRenderable.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/ScreenOverlayImpl.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/entries/EntryListProvider.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/entries/EntryListWidget.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/entries/FavoritesListProvider.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/entries/FavoritesListWidget.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/widgets/DisplayTooltipComponent.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/widgets/DisplayedEntryWidget.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/widgets/OverlayWidgetProvider.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/widgets/RegionRenderingDebugger.java create mode 100644 runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/widgets/ScaleIndicatorWidget.java (limited to 'runtime-frontend/overlay/src/main/java') diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/DraggingContextImpl.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/DraggingContextImpl.java new file mode 100644 index 000000000..ea78fa47a --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/dragging/DraggingContextImpl.java @@ -0,0 +1,311 @@ +/* + * 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.dragging; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.animator.NumberAnimator; +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.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.InternalCursorState; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Supplier; + +@SuppressWarnings("UnstableApiUsage") +public class DraggingContextImpl extends Widget implements DraggingContext { + 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(DraggableComponentProvider provider, DraggableComponentVisitor visitor) { + this.provider = provider; + this.visitor = visitor; + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + Integer hash = null; + + if (entry != null) { + if (!entry.dragging) { + Point startPoint = entry.start; + double xDistance = Math.abs(startPoint.x - mouseX); + double yDistance = Math.abs(startPoint.y - mouseY); + double requiredDistance = 8; + + if (xDistance * xDistance + yDistance * yDistance > requiredDistance * requiredDistance) { + entry.dragging = true; + entry.startDragging = Util.getMillis(); + entry.component.drag(); + } + } + + if (entry.dragging) { + matrices.pushPose(); + matrices.translate(0, 0, 600); + entry.bounds.update(delta); + int width = entry.component.getWidth(); + int height = entry.component.getHeight(); + Vec2 mouseStartOffset = entry.mouseStartOffset; + entry.bounds.setTo(new FloatingRectangle(mouseX - width / 2 - mouseStartOffset.x, mouseY - height / 2 - mouseStartOffset.y, width, height), + 30); + entry.component.render(matrices, entry.bounds.value().getBounds(), mouseX, mouseY, delta); + matrices.popPose(); + + VoxelShape shape = entry.getBoundsProvider().bounds(); + ShapeBounds shapeBounds = new ShapeBounds(shape); + shapeBounds.alpha.setTo(60, 300); + bounds.add(shapeBounds); + hash = shapeBounds.hash; + } + + if (!InternalCursorState.isLeftMousePressed) { + drop(); + } + } + + for (ShapeBounds bound : bounds) { + if ((hash == null || hash != bound.hash) && bound.alpha.target() != 0) { + bound.alpha.setTo(0, 300); + } + } + + { + Iterator iterator = bounds.iterator(); + while (iterator.hasNext()) { + ShapeBounds bounds = iterator.next(); + bounds.update(delta); + if (bounds.alpha.target() == 0 && bounds.alpha.value() <= 2) { + iterator.remove(); + } else { + bounds.shape.forAllBoxes((x1, y1, z1, x2, y2, z2) -> { + matrices.pushPose(); + matrices.translate(0, 0, 500); + fillGradient(matrices, (int) x1, (int) y1, (int) x2, (int) y2, 0xfdff6b | (bounds.alpha.intValue() << 24), 0xfdff6b | (bounds.alpha.intValue() << 24)); + matrices.popPose(); + }); + } + } + } + + Iterator iterator = backToOriginals.iterator(); + while (iterator.hasNext()) { + RenderBackEntry renderBackEntry = iterator.next(); + 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) <= 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.component.render(matrices, value.getBounds(), mouseX, mouseY, delta); + matrices.popPose(); + } + } + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + drop(); + DraggableComponent hoveredStack = provider.getHovered(this, mouseX, mouseY); + if (hoveredStack != null) { + entry = new DraggableEntry(hoveredStack, new Point(mouseX, mouseY)); + } + return false; + } + + @Override + public boolean mouseReleased(double d, double e, int i) { + return false; + } + + @Override + public boolean mouseDragged(double mouseX1, double mouseY1, int button, double mouseX2, double mouseY2) { + return entry != null && entry.dragging; + } + + private boolean drop() { + if (entry != null && entry.dragging) { + DraggedAcceptorResult result = visitor.acceptDragged(this, entry.component); + entry.component.release(result); + entry = null; + return true; + } + + entry = null; + return false; + } + + @Override + public Screen getScreen() { + return Minecraft.getInstance().screen; + } + + @Override + @Nullable + public DraggableStack getCurrentStack() { + 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() { + if (!isDraggingComponent()) return null; + Vec2 mouseStartOffset = entry.mouseStartOffset; + FloatingRectangle rectangle = entry.bounds.value(); + return new Point(rectangle.getCenterX() + mouseStartOffset.x, rectangle.getCenterY() + mouseStartOffset.y); + } + + @Override + @Nullable + public Rectangle getCurrentBounds() { + if (!isDraggingComponent()) return null; + FloatingRectangle rectangle = entry.bounds.value(); + return rectangle.getBounds(); + } + + @Override + 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, width, height); + })); + } + + @Override + public void renderBack(DraggableComponent component, Rectangle initialPosition, Supplier bounds) { + backToOriginals.add(new RenderBackEntry(component, initialPosition, bounds)); + } + + private class DraggableEntry { + private final DraggableComponent component; + private final Point start; + private long startDragging = -1; + private final ValueAnimator bounds; + private final Vec2 mouseStartOffset; + private boolean dragging = false; + private DraggableBoundsProvider boundsProvider; + + private DraggableEntry(DraggableComponent component, Point start) { + this.component = component; + this.start = start; + this.bounds = ValueAnimator.ofFloatingRectangle() + .setAs(component.getOriginBounds(start).getFloatingBounds()); + this.mouseStartOffset = new Vec2((float) (start.x - bounds.value().getCenterX()), (float) (start.y - bounds.value().getCenterY())); + } + + public DraggableBoundsProvider getBoundsProvider() { + if (boundsProvider == null) { + boundsProvider = DraggableBoundsProvider.concat(visitor.getDraggableAcceptingBounds(DraggingContextImpl.this, component).toList()); + } + + return boundsProvider; + } + } + + private static class ShapeBounds { + private final VoxelShape shape; + private final NumberAnimator alpha; + private final int hash; + + public ShapeBounds(VoxelShape shape) { + this.shape = shape; + this.alpha = ValueAnimator.ofDouble(0); + this.hash = shape.toAabbs().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ShapeBounds shapeBounds)) return false; + return hash == shapeBounds.hash; + } + + @Override + public int hashCode() { + return hash; + } + + public void update(double delta) { + this.alpha.update(delta); + } + } + + private static class RenderBackEntry { + private final DraggableComponent component; + private final Supplier position; + private final ValueAnimator bounds = ValueAnimator.ofFloatingRectangle(); + private final int lastDestination = -1; + + public RenderBackEntry(DraggableComponent component, Rectangle initialPosition, Supplier position) { + this.component = component; + this.bounds.setAs(new FloatingRectangle(initialPosition)); + this.position = position; + } + + public Rectangle getPosition() { + return position.get(); + } + + public void update(double delta) { + this.bounds.update(delta); + this.bounds.setTo(new FloatingRectangle(getPosition()), 200); + } + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/AbstractMenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/AbstractMenuEntry.java new file mode 100644 index 000000000..0debff33b --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/AbstractMenuEntry.java @@ -0,0 +1,77 @@ +/* + * 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.menu; + +public abstract class AbstractMenuEntry extends MenuEntry { + private int x, y, width; + private boolean selected, containsMouse, rendering; + + @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 boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isRendering() && mouseX >= getX() && mouseX <= getX() + getWidth() && mouseY >= getY() && mouseY < getY() + getEntryHeight()) { + if (onClick(mouseX, mouseY, button)) { + return true; + } + } + return super.mouseClicked(mouseX, mouseY, button); + } + + protected boolean onClick(double mouseX, double mouseY, int button) { + return false; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getWidth() { + return width; + } + + public boolean isSelected() { + return selected; + } + + public boolean containsMouse() { + return containsMouse; + } + + public boolean isRendering() { + return rendering; + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/Menu.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/Menu.java new file mode 100644 index 000000000..4bbb47671 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/Menu.java @@ -0,0 +1,209 @@ +/* + * 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.menu; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.ClothConfigInitializer; +import me.shedaniel.clothconfig2.api.ScissorsHandler; +import me.shedaniel.clothconfig2.api.scroll.ScrollingContainer; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; +import me.shedaniel.rei.impl.client.gui.menu.entries.SubMenuEntry; +import net.minecraft.client.Minecraft; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; + +@ApiStatus.Internal +public class Menu extends WidgetWithBounds { + public static final UUID WEATHER = UUID.randomUUID(); + public static final UUID GAME_TYPE = UUID.randomUUID(); + + public final Point menuStartPoint; + public final boolean facingRight; + public final boolean facingDownwards; + private final List entries = Lists.newArrayList(); + public final ScrollingContainer scrolling = new ScrollingContainer() { + @Override + public int getMaxScrollHeight() { + int i = 0; + for (MenuEntry entry : children()) { + i += entry.getEntryHeight(); + } + return i; + } + + @Override + public Rectangle getBounds() { + return Menu.this.getInnerBounds(); + } + + @Override + public boolean hasScrollBar() { + return Menu.this.hasScrollBar(); + } + }; + + public Menu(Rectangle menuStart, Collection entries, boolean sort) { + buildEntries(entries, sort); + int fullWidth = Minecraft.getInstance().screen.width; + int fullHeight = Minecraft.getInstance().screen.height; + boolean facingRight = true; + this.facingDownwards = fullHeight - menuStart.getMaxY() > menuStart.y; + int y = facingDownwards ? menuStart.getMaxY() : menuStart.y - (scrolling.getMaxScrollHeight() + 2); + boolean hasScrollBar = scrolling.getMaxScrollHeight() > getInnerHeight(y); + int menuWidth = getMaxEntryWidth() + 2 + (hasScrollBar ? 6 : 0); + if (facingRight && fullWidth - menuStart.getMaxX() < menuWidth + 10) { + facingRight = false; + } else if (!facingRight && menuStart.x < menuWidth + 10) { + facingRight = true; + } + this.facingRight = facingRight; + int x = facingRight ? menuStart.x : menuStart.getMaxX() - (getMaxEntryWidth() + 2 + (hasScrollBar ? 6 : 0)); + this.menuStartPoint = new Point(x, y); + } + + @SuppressWarnings("deprecation") + private void buildEntries(Collection entries, boolean sort) { + this.entries.clear(); + this.entries.addAll(entries); + if (sort) { + this.entries.sort(Comparator.comparing(entry -> entry instanceof SubMenuEntry ? 0 : 1) + .thenComparing(entry -> entry instanceof SubMenuEntry menuEntry ? menuEntry.text.getString() : "")); + } + for (MenuEntry entry : this.entries) { + entry.parent = this; + } + } + + @Override + public Rectangle getBounds() { + return new Rectangle(menuStartPoint.x, menuStartPoint.y, getMaxEntryWidth() + 2 + (hasScrollBar() ? 6 : 0), getInnerHeight(menuStartPoint.y) + 2); + } + + public Rectangle getInnerBounds() { + return new Rectangle(menuStartPoint.x + 1, menuStartPoint.y + 1, getMaxEntryWidth() + (hasScrollBar() ? 6 : 0), getInnerHeight(menuStartPoint.y)); + } + + public boolean hasScrollBar() { + return scrolling.getMaxScrollHeight() > getInnerHeight(menuStartPoint.y); + } + + public int getInnerHeight(int y) { + return Math.min(scrolling.getMaxScrollHeight(), minecraft.screen.height - 20 - y); + } + + public int getMaxEntryWidth() { + int i = 0; + for (MenuEntry entry : children()) { + if (entry.getEntryWidth() > i) + i = entry.getEntryWidth(); + } + return Math.max(10, i); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + Rectangle bounds = getBounds(); + Rectangle innerBounds = getInnerBounds(); + fill(matrices, bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), containsMouse(mouseX, mouseY) ? (ConfigObject.getInstance().isUsingDarkTheme() ? -17587 : -1) : -6250336); + fill(matrices, innerBounds.x, innerBounds.y, innerBounds.getMaxX(), innerBounds.getMaxY(), -16777216); + boolean contains = innerBounds.contains(mouseX, mouseY); + MenuEntry focused = getFocused() instanceof MenuEntry menuEntry ? menuEntry : null; + int currentY = innerBounds.y - scrolling.scrollAmountInt(); + for (MenuEntry child : children()) { + boolean containsMouse = contains && mouseY >= currentY && mouseY < currentY + child.getEntryHeight(); + if (containsMouse) { + focused = child; + } + currentY += child.getEntryHeight(); + } + currentY = innerBounds.y - scrolling.scrollAmountInt(); + ScissorsHandler.INSTANCE.scissor(scrolling.getScissorBounds()); + for (MenuEntry 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(matrices, mouseX, mouseY, delta); + } + currentY += child.getEntryHeight(); + } + ScissorsHandler.INSTANCE.removeLastScissor(); + setFocused(focused); + scrolling.renderScrollBar(0, 1, ConfigObject.getInstance().isUsingDarkTheme() ? 0.8f : 1f); + 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) || getInnerBounds().contains(mouseX, mouseY); + } + + @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 (MenuEntry child : children()) { + if (child instanceof SubMenuEntry) { + if (child.mouseScrolled(mouseX, mouseY, amount)) + return true; + } + } + return super.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public boolean containsMouse(double mouseX, double mouseY) { + if (super.containsMouse(mouseX, mouseY)) return true; + for (MenuEntry child : children()) { + if (child.containsMouse(mouseX, mouseY)) { + return true; + } + } + return false; + } + + @Override + public List children() { + return entries; + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccess.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccess.java new file mode 100644 index 000000000..b890ccd94 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccess.java @@ -0,0 +1,64 @@ +/* + * 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.menu; + +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.math.impl.PointHelper; + +import java.util.Collection; +import java.util.UUID; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public interface MenuAccess { + boolean isOpened(UUID uuid); + + boolean isAnyOpened(); + + boolean isInBounds(UUID uuid); + + void open(UUID uuid, Menu menu); + + void open(UUID uuid, Menu menu, Predicate or, Predicate and); + + default void openOrClose(UUID uuid, Rectangle selfBounds, Supplier> menuSupplier) { + boolean isOpened = isOpened(uuid); + if (isOpened || !isAnyOpened()) { + boolean inBounds = (isValidPoint(PointHelper.ofMouse()) && selfBounds.contains(PointHelper.ofMouse())) || isInBounds(uuid); + if (isOpened != inBounds) { + if (inBounds) { + Menu menu = new Menu(selfBounds.clone(), menuSupplier.get(), false); + open(uuid, menu, selfBounds::contains, point -> true); + } else { + close(); + } + } + } + } + + boolean isValidPoint(Point point); + + void close(); +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccessImpl.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccessImpl.java new file mode 100644 index 000000000..e7be8c3e3 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuAccessImpl.java @@ -0,0 +1,134 @@ +/* + * 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.menu; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.math.Point; +import me.shedaniel.math.impl.PointHelper; +import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.impl.client.gui.overlay.ScreenOverlayImpl; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class MenuAccessImpl implements MenuAccess { + public final List afterRenders = Lists.newArrayList(); + @Nullable + private OverlayMenu menu = null; + + @Override + public boolean isOpened(UUID uuid) { + return menu != null && menu.uuid.equals(uuid); + } + + @Override + public boolean isAnyOpened() { + return menu != null; + } + + @Override + public boolean isInBounds(UUID uuid) { + return isOpened(uuid) && menu.inBounds.test(PointHelper.ofMouse()); + } + + private void proceedOpenMenu(UUID uuid, Runnable runnable) { + proceedOpenMenuOrElse(uuid, runnable, menu -> {}); + } + + private void proceedOpenMenuOrElse(UUID uuid, Runnable runnable, Consumer orElse) { + if (menu == null || !menu.uuid.equals(uuid)) { + close(); + runnable.run(); + } else { + orElse.accept(this.menu); + } + } + + @Override + public void open(UUID uuid, Menu menu) { + open(uuid, menu, point -> false, point -> true); + } + + @Override + public void open(UUID uuid, Menu menu, Predicate or, Predicate and) { + this.menu = new OverlayMenu(uuid, menu, Widgets.withTranslate(menu, 0, 0, 400), or, and); + } + + @Override + public boolean isValidPoint(Point point) { + return ScreenOverlayImpl.getInstance().isNotInExclusionZones(point.x, point.y); + } + + @Override + public void close() { + this.menu = null; + } + + public void afterRender() { + for (Runnable runnable : afterRenders) { + runnable.run(); + } + afterRenders.clear(); + } + + @Nullable + public Widget widget() { + return menu != null ? menu.wrappedMenu : null; + } + + public void lateRender(PoseStack matrices, int mouseX, int mouseY, float delta) { + if (menu != null) { + if (!menu.inBounds.test(PointHelper.ofMouse())) { + close(); + } else { + if (menu.wrappedMenu.containsMouse(mouseX, mouseY)) { + REIRuntime.getInstance().clearTooltips(); + } + menu.wrappedMenu.render(matrices, mouseX, mouseY, delta); + } + } + } + + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + return menu != null && menu.wrappedMenu.mouseScrolled(mouseX, mouseY, amount); + } + + private static class OverlayMenu { + private final UUID uuid; + private final Widget wrappedMenu; + private final Predicate inBounds; + + public OverlayMenu(UUID uuid, Menu menu, Widget wrappedMenu, Predicate or, Predicate and) { + this.uuid = uuid; + this.wrappedMenu = wrappedMenu; + this.inBounds = or.or(menu::containsMouse).and(and); + } + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuEntry.java new file mode 100644 index 000000000..e5c7109d8 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/MenuEntry.java @@ -0,0 +1,44 @@ +/* + * 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.menu; + +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public abstract class MenuEntry extends Widget { + @ApiStatus.Internal + @Deprecated + Menu parent = null; + + public final Menu 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/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/EmptyMenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/EmptyMenuEntry.java new file mode 100644 index 000000000..e269a496d --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/EmptyMenuEntry.java @@ -0,0 +1,58 @@ +/* + * 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.menu.entries; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.rei.impl.client.gui.menu.AbstractMenuEntry; +import net.minecraft.client.gui.components.events.GuiEventListener; + +import java.util.Collections; +import java.util.List; + +public class EmptyMenuEntry extends AbstractMenuEntry { + public int height; + + public EmptyMenuEntry(int height) { + this.height = height; + } + + @Override + public int getEntryWidth() { + return 0; + } + + @Override + public int getEntryHeight() { + return height; + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SeparatorMenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SeparatorMenuEntry.java new file mode 100644 index 000000000..bd0425e74 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SeparatorMenuEntry.java @@ -0,0 +1,53 @@ +/* + * 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.menu.entries; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.rei.impl.client.gui.menu.AbstractMenuEntry; +import net.minecraft.client.gui.components.events.GuiEventListener; + +import java.util.Collections; +import java.util.List; + +public class SeparatorMenuEntry extends AbstractMenuEntry { + @Override + public int getEntryWidth() { + return 0; + } + + @Override + public int getEntryHeight() { + return 5; + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + fillGradient(matrices, getX() + 3, getY() + 2, getX() + getWidth() - 3, getY() + 3, -7829368, -7829368); + } +} \ No newline at end of file diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SubMenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SubMenuEntry.java new file mode 100644 index 000000000..24ae2249c --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/SubMenuEntry.java @@ -0,0 +1,153 @@ +/* + * 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.menu.entries; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Lists; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.ScissorsHandler; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.impl.client.gui.InternalTextures; +import me.shedaniel.rei.api.common.util.ImmutableTextComponent; +import me.shedaniel.rei.impl.client.gui.menu.AbstractMenuEntry; +import me.shedaniel.rei.impl.client.gui.menu.Menu; +import me.shedaniel.rei.impl.client.gui.menu.MenuEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.network.chat.Component; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +public class SubMenuEntry extends AbstractMenuEntry { + public final Component text; + private int textWidth = -69; + protected List entries; + protected Menu childMenu; + + public SubMenuEntry(Component text) { + this(text, Collections.emptyList()); + } + + public SubMenuEntry(Component text, Supplier> entries) { + this(text, entries.get()); + } + + public SubMenuEntry(Component text, List entries) { + this.text = MoreObjects.firstNonNull(text, ImmutableTextComponent.EMPTY); + this.entries = entries; + } + + private int getTextWidth() { + if (textWidth == -69) { + this.textWidth = Math.max(0, font.width(text)); + } + return this.textWidth; + } + + public Menu getChildMenu() { + if (childMenu == null) { + this.childMenu = new Menu(new Rectangle(getParent().getBounds().x + 1, getY() - 1, getParent().getBounds().width - 2, getEntryHeight() - 2), entries, false); + } + return childMenu; + } + + @Override + public int getEntryWidth() { + return 12 + getTextWidth() + 4; + } + + @Override + public int getEntryHeight() { + return 12; + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + renderBackground(poses, getX(), getY(), getWidth(), getEntryHeight()); + if (isSelected()) { + if (!entries.isEmpty()) { + Menu menu = getChildMenu(); + + Rectangle menuStart = new Rectangle(getParent().getBounds().x, getY(), getParent().getBounds().width, getEntryHeight()); + + int fullWidth = Minecraft.getInstance().screen.width; + int fullHeight = Minecraft.getInstance().screen.height; + boolean facingRight = getParent().facingRight; + int menuWidth = menu.getMaxEntryWidth() + 2 + (menu.hasScrollBar() ? 6 : 0); + if (facingRight && fullWidth - menuStart.getMaxX() < menuWidth + 10) { + facingRight = false; + } else if (!facingRight && menuStart.x < menuWidth + 10) { + facingRight = true; + } + boolean facingDownwards = fullHeight - menuStart.getMaxY() > menuStart.y; + + menu.menuStartPoint.y = facingDownwards ? menuStart.y - 1 : menuStart.getMaxY() - (menu.scrolling.getMaxScrollHeight() + 1); + menu.menuStartPoint.x = facingRight ? menuStart.getMaxX() : menuStart.x - (menu.getMaxEntryWidth() + 2 + (menu.scrolling.getMaxScrollHeight() > menu.getInnerHeight(menu.menuStartPoint.y) ? 6 : 0)); + + List areas = Lists.newArrayList(ScissorsHandler.INSTANCE.getScissorsAreas()); + ScissorsHandler.INSTANCE.clearScissors(); + menu.render(poses, mouseX, mouseY, delta); + for (Rectangle area : areas) { + ScissorsHandler.INSTANCE.scissor(area); + } + } + } + font.draw(poses, text, getX() + 2, getY() + 2, isSelected() ? 16777215 : 8947848); + if (!entries.isEmpty()) { + RenderSystem.setShaderTexture(0, InternalTextures.CHEST_GUI_TEXTURE); + blit(poses, getX() + getWidth() - 15, getY() - 2, 0, 28, 18, 18); + } + } + + protected void renderBackground(PoseStack poses, int x, int y, int width, int height) { + if (isSelected()) { + fill(poses, x, y, x + width, y + height, -12237499); + } + } + + @Override + public boolean containsMouse(double mouseX, double mouseY) { + if (super.containsMouse(mouseX, mouseY)) + return true; + if (childMenu != null && !childMenu.children().isEmpty() && isSelected()) + return childMenu.containsMouse(mouseX, mouseY); + return false; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + return childMenu != null && !childMenu.children().isEmpty() && isSelected() && childMenu.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public List children() { + if (childMenu != null && !childMenu.children().isEmpty() && isSelected()) { + return Collections.singletonList(childMenu); + } + return Collections.emptyList(); + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/TextMenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/TextMenuEntry.java new file mode 100644 index 000000000..57fb07d55 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/TextMenuEntry.java @@ -0,0 +1,70 @@ +/* + * 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.menu.entries; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.rei.impl.client.gui.menu.AbstractMenuEntry; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.network.chat.Component; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +public class TextMenuEntry extends AbstractMenuEntry { + public final Supplier text; + public Component lastText; + public int lastTextWidth; + + public TextMenuEntry(Supplier text) { + this.text = text; + this.lastText = text.get(); + this.lastTextWidth = Math.max(0, font.width(lastText)); + } + + private int getTextWidth() { + return lastTextWidth; + } + + @Override + public int getEntryWidth() { + return getTextWidth() + 4; + } + + @Override + public int getEntryHeight() { + return 12; + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + font.draw(matrices, lastText = text.get(), getX() + 2, getY() + 2, 8947848); + this.lastTextWidth = Math.max(0, font.width(lastText)); + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/ToggleMenuEntry.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/ToggleMenuEntry.java new file mode 100644 index 000000000..3ac2cb627 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/menu/entries/ToggleMenuEntry.java @@ -0,0 +1,143 @@ +/* + * 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.menu.entries; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import it.unimi.dsi.fastutil.booleans.BooleanConsumer; +import me.shedaniel.clothconfig2.api.ScissorsHandler; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.config.ConfigManager; +import me.shedaniel.rei.api.client.gui.widgets.Tooltip; +import me.shedaniel.rei.api.client.overlay.ScreenOverlay; +import me.shedaniel.rei.impl.client.gui.menu.AbstractMenuEntry; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; + +import java.util.Collections; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public class ToggleMenuEntry extends AbstractMenuEntry { + public final Component text; + public final BooleanSupplier supplier; + public final BooleanUnaryOperator consumer; + public BooleanSupplier active = () -> true; + public Supplier tooltip = () -> null; + private int textWidth = -69; + + public static ToggleMenuEntry of(Component text, BooleanSupplier supplier, BooleanConsumer consumer) { + return new ToggleMenuEntry(text, supplier, b -> { + consumer.accept(b); + return true; + }); + } + + public static ToggleMenuEntry ofDeciding(Component text, BooleanSupplier supplier, BooleanUnaryOperator consumer) { + return new ToggleMenuEntry(text, supplier, consumer); + } + + protected ToggleMenuEntry(Component text, BooleanSupplier supplier, BooleanUnaryOperator consumer) { + this.text = text; + this.supplier = supplier; + this.consumer = consumer; + } + + public ToggleMenuEntry withActive(BooleanSupplier active) { + this.active = active; + return this; + } + + public ToggleMenuEntry withTooltip(Supplier tooltip) { + this.tooltip = tooltip; + return this; + } + + @FunctionalInterface + public interface BooleanUnaryOperator { + boolean apply(boolean b); + } + + private int getTextWidth() { + if (textWidth == -69) { + this.textWidth = Math.max(0, font.width(text)); + } + return this.textWidth; + } + + @Override + public int getEntryWidth() { + return getTextWidth() + 4 + 8; + } + + @Override + public int getEntryHeight() { + return 12; + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + if (isSelected() && active.getAsBoolean()) { + fill(matrices, getX(), getY(), getX() + getWidth(), getY() + getEntryHeight(), -12237499); + + Tooltip tooltip = this.tooltip.get(); + + if (tooltip != null) { + List areas = Lists.newArrayList(ScissorsHandler.INSTANCE.getScissorsAreas()); + ScissorsHandler.INSTANCE.clearScissors(); + matrices.pushPose(); + matrices.translate(0, 0, -400); + REIRuntime.getInstance().getOverlay().get().renderTooltip(matrices, tooltip); + matrices.popPose(); + for (Rectangle area : areas) { + ScissorsHandler.INSTANCE.scissor(area); + } + } + } + font.draw(matrices, text, getX() + 2, getY() + 2, isSelected() && active.getAsBoolean() ? 16777215 : 8947848); + if (supplier.getAsBoolean()) { + font.draw(matrices, "✔", getX() + getWidth() - 2 - font.width("✔"), getY() + 2, isSelected() && active.getAsBoolean() ? 16777215 : 8947848); + } + } + + @Override + protected boolean onClick(double mouseX, double mouseY, int button) { + if (!active.getAsBoolean()) return false; + if (consumer.apply(!supplier.getAsBoolean())) { + REIRuntime.getInstance().getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); + } + ConfigManager.getInstance().saveConfig(); + minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + return true; + } +} diff --git a/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/AbstractScreenOverlay.java b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/AbstractScreenOverlay.java new file mode 100644 index 000000000..611a55748 --- /dev/null +++ b/runtime-frontend/overlay/src/main/java/me/shedaniel/rei/impl/client/gui/overlay/AbstractScreenOverlay.java @@ -0,0 +1,330 @@ +/* + * 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.overlay; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.math.impl.PointHelper; +import me.shedaniel.rei.