From 8a13f5347f6d7cd70b37a62618f8627e2ffaf277 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 7 Nov 2021 18:01:38 +0800 Subject: Modularize region rendering --- .../rei/RoughlyEnoughItemsCoreClient.java | 1 + .../shedaniel/rei/impl/client/REIRuntimeImpl.java | 6 +- .../impl/client/gui/craftable/CraftableFilter.java | 2 +- .../impl/client/gui/widget/EntryListWidget.java | 2 +- .../client/gui/widget/EntryStacksRegionWidget.java | 451 +++++++++++++++ .../client/gui/widget/FavoritesListWidget.java | 624 ++------------------- .../client/gui/widget/region/RealRegionEntry.java | 93 +++ .../gui/widget/region/RegionDraggableStack.java | 71 +++ .../gui/widget/region/RegionEntryListEntry.java | 143 +++++ .../client/gui/widget/region/RegionListener.java | 41 ++ .../client/registry/screen/ScreenRegistryImpl.java | 1 + .../comparison/EntryComparatorRegistryImpl.java | 8 +- 12 files changed, 869 insertions(+), 574 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RealRegionEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionDraggableStack.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionEntryListEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionListener.java (limited to 'runtime/src/main/java/me') diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java index f5c8688ea..116beb408 100644 --- a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java @@ -282,6 +282,7 @@ public class RoughlyEnoughItemsCoreClient { reloadPlugins(endReload, Platform.isFabric() ? ReloadStage.END : null); }); GuiEvent.INIT_POST.register((screen, widgets, children) -> { + REIRuntime.getInstance().getOverlay(false, true); REIRuntimeImpl.getInstance().setPreviousScreen(screen); if (ConfigObject.getInstance().doesDisableRecipeBook() && screen instanceof AbstractContainerScreen) { widgets.removeIf(widget -> widget instanceof ImageButton && ((ImageButton) widget).resourceLocation.equals(recipeButtonTex)); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java index 2f9a96c29..f293beb0c 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java @@ -137,8 +137,8 @@ public class REIRuntimeImpl implements REIRuntime { } @Override - public Optional getOverlay(boolean reset) { - if (overlay == null || reset) { + public Optional getOverlay(boolean reset, boolean init) { + if ((overlay == null && init) || reset) { overlay = new ScreenOverlayImpl(); overlay.init(); getSearchField().setFocused(false); @@ -240,7 +240,7 @@ public class REIRuntimeImpl implements REIRuntime { return InteractionResult.PASS; }); ClientTickEvent.CLIENT_POST.register(minecraft -> { - if (isOverlayVisible()) { + if (isOverlayVisible() && REIRuntime.getInstance().getOverlay().isPresent()) { ScreenOverlayImpl.getInstance().tick(); } }); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java index 0bd0765d8..f0f8a47c1 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java @@ -48,7 +48,7 @@ public class CraftableFilter { return true; } - return Minecraft.getInstance().player.containerMenu != null; + return false; } public void tick() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java index 1763e2996..401cfda02 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java @@ -420,7 +420,7 @@ public class EntryListWidget extends WidgetWithBounds implements OverlayListWidg } FavoritesListWidget favoritesListWidget = ScreenOverlayImpl.getFavoritesListWidget(); if (favoritesListWidget != null) { - favoritesListWidget.updateEntriesPosition(entry -> true); + favoritesListWidget.getRegion().updateEntriesPosition(entry -> true); } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java new file mode 100644 index 000000000..f403ef6fe --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java @@ -0,0 +1,451 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021 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.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import me.shedaniel.clothconfig2.ClothConfigInitializer; +import me.shedaniel.clothconfig2.api.ScissorsHandler; +import me.shedaniel.clothconfig2.api.ScrollingContainer; +import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.math.impl.PointHelper; +import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.entry.region.RegionEntry; +import me.shedaniel.rei.api.client.gui.drag.DraggableStack; +import me.shedaniel.rei.api.client.gui.drag.DraggableStackVisitorWidget; +import me.shedaniel.rei.api.client.gui.drag.DraggingContext; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; +import me.shedaniel.rei.api.common.entry.EntrySerializer; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.gui.widget.region.RealRegionEntry; +import me.shedaniel.rei.impl.client.gui.widget.region.RegionDraggableStack; +import me.shedaniel.rei.impl.client.gui.widget.region.RegionEntryListEntry; +import me.shedaniel.rei.impl.client.gui.widget.region.RegionListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.util.Mth; +import net.minecraft.util.Tuple; +import net.minecraft.util.Unit; +import org.jetbrains.annotations.Nullable; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static me.shedaniel.rei.impl.client.gui.widget.EntryListWidget.entrySize; +import static me.shedaniel.rei.impl.client.gui.widget.EntryListWidget.notSteppingOnExclusionZones; + +public class EntryStacksRegionWidget> extends WidgetWithBounds implements DraggableStackVisitorWidget { + private final RegionListener listener; + protected int blockedCount; + private Rectangle bounds = new Rectangle(), innerBounds; + protected final ScrollingContainer scrolling = new ScrollingContainer() { + @Override + public Rectangle getBounds() { + return EntryStacksRegionWidget.this.getBounds(); + } + + @Override + public int getMaxScrollHeight() { + return Mth.ceil((entries.size() + blockedCount) / (innerBounds.width / (float) entrySize())) * entrySize(); + } + + @Override + public int getScrollBarX() { + if (!ConfigObject.getInstance().isLeftHandSidePanel()) + return bounds.x + 1; + return bounds.getMaxX() - 7; + } + }; + private final Int2ObjectMap> entries = new Int2ObjectLinkedOpenHashMap<>(); + private final Int2ObjectMap> removedEntries = new Int2ObjectLinkedOpenHashMap<>(); + private List> entriesList = Lists.newArrayList(); + private List children = Lists.newArrayList(); + + public EntryStacksRegionWidget(RegionListener listener) { + this.listener = listener; + } + + @Override + public Rectangle getBounds() { + return bounds; + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + if (bounds.isEmpty()) return; + + int entrySize = entrySize(); + boolean fastEntryRendering = ConfigObject.getInstance().doesFastEntryRendering(); + updateEntriesPosition(entry -> true); + for (RealRegionEntry entry : entries.values()) { + entry.update(delta); + } + ObjectIterator> removedEntriesIterator = removedEntries.values().iterator(); + while (removedEntriesIterator.hasNext()) { + RealRegionEntry removedEntry = removedEntriesIterator.next(); + removedEntry.update(delta); + + if (removedEntry.size.doubleValue() <= 300) { + removedEntriesIterator.remove(); + this.entriesList.remove(removedEntry.getWidget()); + this.children.remove(removedEntry.getWidget()); + } + } + + ScissorsHandler.INSTANCE.scissor(bounds); + + Stream> entryStream = this.entriesList.stream() + .filter(entry -> entry.getBounds().getMaxY() >= this.bounds.getY() && entry.getBounds().y <= this.bounds.getMaxY()); + + new BatchedEntryRendererManager(entryStream.collect(Collectors.toList())) + .render(poses, mouseX, mouseY, delta); + + updatePosition(delta); + scrolling.renderScrollBar(0, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f); + ScissorsHandler.INSTANCE.removeLastScissor(); + } + + @Override + public List children() { + return children; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (scrolling.updateDraggingState(mouseX, mouseY, button)) { + return true; + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (containsMouse(mouseX, mouseY)) { + scrolling.offset(ClothConfigInitializer.getScrollStep() * -amount, true); + return true; + } + return super.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (scrolling.mouseDragged(mouseX, mouseY, button, deltaX, deltaY, ConfigObject.getInstance().doesSnapToRows(), entrySize())) + return true; + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + private void updatePosition(float delta) { + if (ConfigObject.getInstance().doesSnapToRows() && scrolling.scrollTarget >= 0 && scrolling.scrollTarget <= scrolling.getMaxScroll()) { + double nearestRow = Math.round(scrolling.scrollTarget / (double) entrySize()) * (double) entrySize(); + if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(scrolling.scrollTarget, nearestRow, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON)) + scrolling.scrollTarget += (nearestRow - scrolling.scrollTarget) * Math.min(delta / 2.0, 1.0); + else + scrolling.scrollTarget = nearestRow; + } + scrolling.updatePosition(delta); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (containsMouse(PointHelper.ofMouse())) + for (Widget widget : children()) + if (widget.keyPressed(keyCode, scanCode, modifiers)) + return true; + return false; + } + + @Nullable + public DraggableStack getHoveredStack(DraggingContext context, double mouseX, double mouseY) { + if (innerBounds.contains(mouseX, mouseY)) { + for (RealRegionEntry entry : entries.values()) { + if (entry.getWidget().containsMouse(mouseX, mouseY)) { + return new RegionDraggableStack<>(entry, null); + } + } + } + return null; + } + + public EntryStack getFocusedStack() { + Point mouse = PointHelper.ofMouse(); + if (innerBounds.contains(mouse)) { + for (RealRegionEntry entry : entries.values()) { + if (entry.getWidget().containsMouse(mouse)) { + return entry.getWidget().getCurrentEntry().copy(); + } + } + } + return EntryStack.empty(); + } + + public Stream> getEntries() { + return (Stream>) (Stream>) entriesList.stream() + .filter(entry -> entry.getBounds().getMaxY() >= this.bounds.getY() && entry.getBounds().y <= this.bounds.getMaxY()) + .map(EntryWidget::getCurrentEntry) + .filter(entry -> !entry.isEmpty()); + } + + @Override + public boolean acceptDraggedStack(DraggingContext context, DraggableStack stack) { + return checkDraggedStacks(context, stack) + .filter(entry -> innerBounds.contains(context.getCurrentPosition())) + .map(entry -> { + drop(entry); + return Unit.INSTANCE; + }).isPresent(); + } + + public Optional> checkDraggedStacks(DraggingContext context, DraggableStack stack) { + EntrySerializer serializer = stack.getStack().getDefinition().getSerializer(); + if (stack instanceof RegionDraggableStack || (serializer.supportReading() && serializer.supportSaving())) { + try { + T regionEntry = stack instanceof RegionDraggableStack ? ((RegionDraggableStack) stack).getEntry().getEntry().copy() + : listener.convertDraggableStack(context, stack); + if (regionEntry == null) return Optional.empty(); + RealRegionEntry entry = new RealRegionEntry<>(this, regionEntry, entrySize()); + entry.size.setAs(entrySize() * 100); + return Optional.of(entry); + } catch (Throwable ignored) { + } + } + return Optional.empty(); + } + + public void setEntries(List newEntries) { + newEntries = Lists.newArrayList(newEntries); + newEntries.removeIf(entry -> entry == null || entry.isEntryInvalid()); + + int entrySize = entrySize(); + IntSet newFavoritesHash = new IntOpenHashSet(CollectionUtils.mapToInt(newEntries, T::hashCode)); + List> removedEntries = Lists.newArrayList(this.entries.values()); + removedEntries.removeIf(entry -> newFavoritesHash.contains(entry.hashIgnoreAmount())); + + for (RealRegionEntry removedEntry : removedEntries) { + removedEntry.remove(); + this.removedEntries.put(removedEntry.hashIgnoreAmount(), removedEntry); + } + + Int2ObjectMap> prevEntries = new Int2ObjectOpenHashMap<>(entries); + this.entries.clear(); + + for (T regionEntry : newEntries) { + RealRegionEntry realEntry = prevEntries.get(regionEntry.hashCode()); + + if (realEntry == null) { + realEntry = new RealRegionEntry<>(this, regionEntry, entrySize); + } + + if (!ConfigObject.getInstance().isFavoritesAnimated()) realEntry.size.setAs(entrySize * 100); + else realEntry.size.setTo(entrySize * 100, 300); + entries.put(realEntry.hashIgnoreAmount(), realEntry); + } + + applyNewEntriesList(); + updateEntriesPosition(entry -> prevEntries.containsKey(entry.hashIgnoreAmount())); + } + + public void applyNewEntriesList() { + this.entriesList = Stream.concat(entries.values().stream().map(RealRegionEntry::getWidget), removedEntries.values().stream().map(RealRegionEntry::getWidget)).collect(Collectors.toList()); + this.children = Stream.>of( + entries.values().stream().map(RealRegionEntry::getWidget), + removedEntries.values().stream().map(RealRegionEntry::getWidget) + ).flatMap(Function.identity()).collect(Collectors.toList()); + } + + public void updateEntriesPosition(Predicate> animated) { + int entrySize = entrySize(); + this.blockedCount = 0; + this.innerBounds = updateInnerBounds(bounds); + int width = innerBounds.width / entrySize; + int currentX = 0; + int currentY = 0; + int releaseIndex = getReleaseIndex(); + + int slotIndex = 0; + for (RealRegionEntry entry : this.entries.values()) { + while (true) { + int xPos = currentX * entrySize + innerBounds.x; + int yPos = currentY * entrySize + innerBounds.y; + + currentX++; + if (currentX >= width) { + currentX = 0; + currentY++; + } + + if (notSteppingOnExclusionZones(xPos, yPos - (int) scrolling.scrollAmount, entrySize, entrySize, innerBounds)) { + if (slotIndex++ == releaseIndex) { + continue; + } + + entry.moveTo(animated.test(entry), xPos, yPos); + break; + } else { + blockedCount++; + } + } + } + } + + private int getReleaseIndex() { + DraggingContext context = DraggingContext.getInstance(); + Point position = context.getCurrentPosition(); + if (context.isDraggingStack() && bounds.contains(position) && checkDraggedStacks(context.cast(), context.getCurrentStack()).isPresent()) { + int entrySize = entrySize(); + int width = innerBounds.width / entrySize; + int currentX = 0; + int currentY = 0; + List, Point>> entriesPoints = Lists.newArrayList(); + for (RealRegionEntry entry : this.entries.values()) { + while (true) { + int xPos = currentX * entrySize + innerBounds.x; + int yPos = currentY * entrySize + innerBounds.y; + + currentX++; + if (currentX >= width) { + currentX = 0; + currentY++; + } + + if (notSteppingOnExclusionZones(xPos, yPos - (int) scrolling.scrollAmount, entrySize, entrySize, innerBounds)) { + entriesPoints.add(new Tuple<>(entry, new Point(xPos, yPos))); + break; + } else { + blockedCount++; + } + } + } + + int maxSize = entriesPoints.size(); + if (currentX != 0) { + int xPos = currentX * entrySize + innerBounds.x; + int yPos = currentY * entrySize + innerBounds.y; + + if (notSteppingOnExclusionZones(xPos, yPos - (int) scrolling.scrollAmount, entrySize, entrySize, innerBounds)) { + entriesPoints.add(new Tuple<>(null, new Point(xPos, yPos))); + } + } + + double x = position.x - 8; + double y = position.y + scrolling.scrollAmount - 8; + + return Mth.clamp(entriesPoints.stream() + .filter(value -> { + double otherY = value.getB().y; + + return otherY <= y + entrySize / 2 && otherY + entrySize > y + entrySize / 2; + }) + .min(Comparator.comparingDouble(value -> { + double otherX = value.getB().x; + double otherY = value.getB().y; + + return (x - otherX) * (x - otherX) + (y - otherY) * (y - otherY); + })) + .map(entriesPoints::indexOf) + .orElse(maxSize), + 0, entriesPoints.size()); + } + + return -2; + } + + private static Rectangle updateInnerBounds(Rectangle bounds) { + int entrySize = entrySize(); + int width = Math.max(Mth.floor((bounds.width - 2 - 6) / (float) entrySize), 1); + if (!ConfigObject.getInstance().isLeftHandSidePanel()) + return new Rectangle((int) (bounds.getCenterX() - width * (entrySize / 2f) + 3), bounds.y, width * entrySize, bounds.height); + return new Rectangle((int) (bounds.getCenterX() - width * (entrySize / 2f) - 3), bounds.y, width * entrySize, bounds.height); + } + + public void drop(RealRegionEntry entry) { + DraggingContext context = DraggingContext.getInstance(); + double x = context.getCurrentPosition().x; + double y = context.getCurrentPosition().y + scrolling.scrollAmount; + entry.startedDraggingPosition = null; + + entry.x.setAs(x - 8); + entry.y.setAs(y - 8); + + boolean contains = bounds.contains(PointHelper.ofMouse()); + int newIndex = contains ? getReleaseIndex() : Math.max(0, Iterables.indexOf(entries.values(), e -> e == entry)); + + if (entries.size() - 1 <= newIndex) { + RealRegionEntry remove = this.entries.remove(entry.hashIgnoreAmount()); + if (remove != null) { + remove.remove(); + this.removedEntries.put(remove.hashIgnoreAmount(), remove); + } + this.entries.put(entry.hashIgnoreAmount(), entry); + } else { + Int2ObjectMap> prevEntries = new Int2ObjectLinkedOpenHashMap<>(entries); + this.entries.clear(); + + int index = 0; + for (Int2ObjectMap.Entry> entryEntry : prevEntries.int2ObjectEntrySet()) { + if (index == newIndex) { + this.entries.put(entry.hashIgnoreAmount(), entry); + } + if (entryEntry.getIntKey() != entry.hashIgnoreAmount()) { + this.entries.put(entryEntry.getIntKey(), entryEntry.getValue()); + index++; + } + } + } + + applyNewEntriesList(); + + listener.onDrop(this.entries.values().stream() + .map(RealRegionEntry::getEntry)); + + setEntries(this.entries.values().stream() + .map(RealRegionEntry::getEntry) + .collect(Collectors.toList())); + } + + public void remove(RealRegionEntry entry) { + entries.remove(entry.hashIgnoreAmount()); + setEntries(CollectionUtils.map(entries.values(), RealRegionEntry::getEntry)); + } + + public double getScrollAmount() { + return scrolling.scrollAmount; + } + + public boolean has(RealRegionEntry entry) { + int hash = entry.hashIgnoreAmount(); + return entries.containsKey(hash) && !removedEntries.containsKey(hash); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/FavoritesListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/FavoritesListWidget.java index 27c43710b..3729da97d 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/FavoritesListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/FavoritesListWidget.java @@ -23,18 +23,13 @@ package me.shedaniel.rei.impl.client.gui.widget; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.math.Vector4f; -import it.unimi.dsi.fastutil.ints.*; -import it.unimi.dsi.fastutil.objects.ObjectIterator; import me.shedaniel.clothconfig2.ClothConfigInitializer; import me.shedaniel.clothconfig2.api.LazyResettable; import me.shedaniel.clothconfig2.api.ScissorsHandler; import me.shedaniel.clothconfig2.api.ScrollingContainer; -import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; import me.shedaniel.math.impl.PointHelper; @@ -43,26 +38,25 @@ 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.favorites.FavoriteEntryType; -import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.gui.AbstractContainerEventHandler; -import me.shedaniel.rei.api.client.gui.drag.*; +import me.shedaniel.rei.api.client.gui.drag.DraggableStack; +import me.shedaniel.rei.api.client.gui.drag.DraggableStackProviderWidget; +import me.shedaniel.rei.api.client.gui.drag.DraggingContext; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; import me.shedaniel.rei.api.client.overlay.OverlayListWidget; import me.shedaniel.rei.api.client.overlay.ScreenOverlay; -import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry; import me.shedaniel.rei.api.client.util.ClientEntryStacks; -import me.shedaniel.rei.api.common.entry.EntrySerializer; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.util.Animator; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.api.common.util.ImmutableTextComponent; import me.shedaniel.rei.impl.client.config.ConfigManagerImpl; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; -import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; -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.widget.region.RealRegionEntry; +import me.shedaniel.rei.impl.client.gui.widget.region.RegionDraggableStack; +import me.shedaniel.rei.impl.client.gui.widget.region.RegionListener; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; @@ -70,81 +64,42 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.util.Mth; -import net.minecraft.util.Tuple; -import net.minecraft.util.Unit; -import net.minecraft.world.phys.shapes.BooleanOp; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; -import java.util.*; -import java.util.function.Function; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; import static me.shedaniel.rei.impl.client.gui.widget.EntryListWidget.entrySize; import static me.shedaniel.rei.impl.client.gui.widget.EntryListWidget.notSteppingOnExclusionZones; @ApiStatus.Internal -public class FavoritesListWidget extends WidgetWithBounds implements DraggableStackProviderWidget, DraggableStackVisitorWidget, OverlayListWidget { - protected final ScrollingContainer scrolling = new ScrollingContainer() { - @Override - public Rectangle getBounds() { - return currentBounds; - } - - @Override - public int getMaxScrollHeight() { - return Mth.ceil((entries.size() + blockedCount) / (innerBounds.width / (float) entrySize())) * entrySize(); - } - - @Override - public int getScrollBarX() { - if (!ConfigObject.getInstance().isLeftHandSidePanel()) - return fullBounds.x + 1; - return fullBounds.getMaxX() - 7; - } - }; - protected int blockedCount; - private Rectangle fullBounds, currentBounds = new Rectangle(), innerBounds; - private final Int2ObjectMap entries = new Int2ObjectLinkedOpenHashMap<>(); - private final Int2ObjectMap removedEntries = new Int2ObjectLinkedOpenHashMap<>(); - private List entriesList = Lists.newArrayList(); - private List children = Lists.newArrayList(); +public class FavoritesListWidget extends WidgetWithBounds implements DraggableStackProviderWidget, OverlayListWidget, RegionListener { + private Rectangle fullBounds; + private EntryStacksRegionWidget region = new EntryStacksRegionWidget<>(this); public final AddFavoritePanel favoritePanel = new AddFavoritePanel(this); public final ToggleAddFavoritePanelButton favoritePanelButton = new ToggleAddFavoritePanelButton(this); - - private static Rectangle updateInnerBounds(Rectangle bounds) { - int entrySize = entrySize(); - int width = Math.max(Mth.floor((bounds.width - 2 - 6) / (float) entrySize), 1); - if (!ConfigObject.getInstance().isLeftHandSidePanel()) - return new Rectangle((int) (bounds.getCenterX() - width * (entrySize / 2f) + 3), bounds.y, width * entrySize, bounds.height); - return new Rectangle((int) (bounds.getCenterX() - width * (entrySize / 2f) - 3), bounds.y, width * entrySize, bounds.height); - } + private List children = ImmutableList.of(favoritePanel, favoritePanelButton, region); @Override - public boolean mouseScrolled(double double_1, double double_2, double double_3) { - if (currentBounds.contains(double_1, double_2)) { + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (fullBounds.contains(mouseX, mouseY)) { if (Screen.hasControlDown()) { ConfigObjectImpl config = ConfigManagerImpl.getInstance().getConfig(); - if (config.setEntrySize(config.getEntrySize() + double_3 * 0.075)) { + if (config.setEntrySize(config.getEntrySize() + amount * 0.075)) { ConfigManager.getInstance().saveConfig(); REIRuntime.getInstance().getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); return true; } - } else { - if (favoritePanel.mouseScrolled(double_1, double_2, double_3)) { - return true; - } - scrolling.offset(ClothConfigInitializer.getScrollStep() * -double_3, true); + } else if (favoritePanel.mouseScrolled(mouseX, mouseY, amount)) { return true; } } - return super.mouseScrolled(double_1, double_2, double_3); + return super.mouseScrolled(mouseX, mouseY, amount); } @Override @@ -152,24 +107,42 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt return fullBounds; } + public EntryStacksRegionWidget getRegion() { + return region; + } + + @Override + public void onDrop(Stream entries) { + if (ConfigObject.getInstance().isFavoritesEnabled()) { + List favorites = ConfigObject.getInstance().getFavoriteEntries(); + favorites.clear(); + entries.forEach(entry -> { + favorites.add(entry.copy()); + }); + + ConfigManager.getInstance().saveConfig(); + } + } + + @Override + @Nullable + public FavoriteEntry convertDraggableStack(DraggingContext context, DraggableStack stack) { + return FavoriteEntry.fromEntryStack(stack.getStack().copy()); + } + @Override @Nullable public DraggableStack getHoveredStack(DraggingContext context, double mouseX, double mouseY) { - if (innerBounds.contains(mouseX, mouseY)) { - for (Entry entry : entries.values()) { - if (entry.getWidget().containsMouse(mouseX, mouseY)) { - return new FavoriteDraggableStack(entry, null); - } - } - } + DraggableStack stack = region.getHoveredStack(context, mouseX, mouseY); + if (stack != null) return stack; if (favoritePanel.bounds.contains(mouseX, mouseY)) { for (AddFavoritePanel.Row row : favoritePanel.rows.get()) { if (row instanceof AddFavoritePanel.SectionEntriesRow) { for (AddFavoritePanel.SectionEntriesRow.SectionFavoriteWidget widget : ((AddFavoritePanel.SectionEntriesRow) row).widgets) { if (widget.containsMouse(mouseX, mouseY)) { - Entry entry = new Entry(widget.entry.copy(), entrySize()); + RealRegionEntry entry = new RealRegionEntry<>(region, widget.entry.copy(), entrySize()); entry.size.setAs(entrySize() * 100); - return new FavoriteDraggableStack(entry, widget); + return new RegionDraggableStack<>(entry, widget); } } } @@ -181,13 +154,8 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt @Override public EntryStack getFocusedStack() { Point mouse = PointHelper.ofMouse(); - if (innerBounds.contains(mouse)) { - for (Entry entry : entries.values()) { - if (entry.getWidget().containsMouse(mouse)) { - return entry.getWidget().getCurrentEntry().copy(); - } - } - } + EntryStack stack = region.getFocusedStack(); + if (stack != null && !stack.isEmpty()) return stack; if (favoritePanel.bounds.contains(mouse)) { for (AddFavoritePanel.Row row : favoritePanel.rows.get()) { if (row instanceof AddFavoritePanel.SectionEntriesRow) { @@ -204,123 +172,18 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt @Override public Stream> getEntries() { - return (Stream>) (Stream>) entriesList.stream() - .filter(entry -> entry.getBounds().getMaxY() >= this.currentBounds.getY() && entry.getBounds().y <= this.currentBounds.getMaxY()) - .map(EntryWidget::getCurrentEntry) - .filter(entry -> !entry.isEmpty()); - } - - public class FavoriteDraggableStack implements DraggableStack { - private Entry entry; - private FavoriteEntry favoriteEntry; - private EntryStack stack; - private WidgetWithBounds showcaseWidget; - - public FavoriteDraggableStack(Entry entry, WidgetWithBounds showcaseWidget) { - this.entry = entry; - this.favoriteEntry = entry.getEntry(); - this.stack = ClientEntryStacks.of(favoriteEntry.getRenderer(false)); - this.showcaseWidget = showcaseWidget; - } - - @Override - public EntryStack getStack() { - return stack; - } - - @Override - public void drag() { - if (showcaseWidget == null) { - entries.remove(entry.hashIgnoreAmount()); - applyNewFavorites(CollectionUtils.map(entries.values(), Entry::getEntry)); - } - } - - @Override - public void release(boolean accepted) { - if (!accepted) { - if (showcaseWidget != null) { - DraggingContext.getInstance().renderBackToPosition(this, DraggingContext.getInstance().getCurrentPosition(), - () -> new Point(showcaseWidget.getBounds().x, showcaseWidget.getBounds().y)); - } else { - drop(entry, this, favoriteEntry); - } - } - } - } - - public Optional> checkDraggedStacks(DraggingContext context, DraggableStack stack) { - EntrySerializer serializer = stack.getStack().getDefinition().getSerializer(); - if (stack instanceof FavoriteDraggableStack || (serializer.supportReading() && serializer.supportSaving())) { - try { - FavoriteEntry favoriteEntry = stack instanceof FavoriteDraggableStack ? ((FavoriteDraggableStack) stack).favoriteEntry.copy() - : FavoriteEntry.fromEntryStack(stack.getStack().copy()); - Entry entry = new Entry(favoriteEntry, entrySize()); - entry.size.setAs(entrySize() * 100); - return Optional.of(new Tuple<>(entry, favoriteEntry)); - } catch (Throwable ignored) { - } - } - return Optional.empty(); - } - - @Override - public boolean acceptDraggedStack(DraggingContext context, DraggableStack stack) { - return checkDraggedStacks(context, stack) - .filter(tuple -> innerBounds.contains(context.getCurrentPosition())) - .map(tuple -> { - drop(tuple.getA(), stack, tuple.getB()); - return Unit.INSTANCE; - }).isPresent(); - } - - @Override - public Stream getDraggableAcceptingBounds(DraggingContext context, DraggableStack stack) { - return checkDraggedStacks(context, stack).isPresent() ? Stream.of(DraggableStackVisitor.BoundsProvider.ofShape(buildBounds())) : Stream.empty(); - } - - private VoxelShape buildBounds() { - Class screenClass = Minecraft.getInstance().screen.getClass(); - VoxelShape shape = DraggableStackVisitor.BoundsProvider.fromRectangle(innerBounds); - for (Rectangle zone : ScreenRegistry.getInstance().exclusionZones().getExclusionZones(screenClass)) { - shape = Shapes.joinUnoptimized(shape, DraggableStackVisitor.BoundsProvider.fromRectangle(zone), BooleanOp.ONLY_FIRST); - } - return shape.optimize(); + return region.getEntries(); } @Override public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { - if (fullBounds.isEmpty() || currentBounds.isEmpty()) + if (fullBounds.isEmpty()) return; - int entrySize = entrySize(); - boolean fastEntryRendering = ConfigObject.getInstance().doesFastEntryRendering(); - updateEntriesPosition(entry -> true); - for (Entry entry : entries.values()) { - entry.update(delta); - } - ObjectIterator removedEntriesIterator = removedEntries.values().iterator(); - while (removedEntriesIterator.hasNext()) { - Entry removedEntry = removedEntriesIterator.next(); - removedEntry.update(delta); - - if (removedEntry.size.doubleValue() <= 300) { - removedEntriesIterator.remove(); - this.entriesList.remove(removedEntry.getWidget()); - this.children.remove(removedEntry.getWidget()); - } - } - ScissorsHandler.INSTANCE.scissor(currentBounds); - - Stream entryStream = this.entriesList.stream() - .filter(entry -> entry.getBounds().getMaxY() >= this.currentBounds.getY() && entry.getBounds().y <= this.currentBounds.getMaxY()); - - new BatchedEntryRendererManager(entryStream.collect(Collectors.toList())) - .render(matrices, mouseX, mouseY, delta); - - updatePosition(delta); - scrolling.renderScrollBar(0, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f); - ScissorsHandler.INSTANCE.removeLastScissor(); + if (favoritePanel.getBounds().height > 20) + region.getBounds().setBounds(this.fullBounds.x, this.fullBounds.y, this.fullBounds.width, this.fullBounds.height - (this.fullBounds.getMaxY() - this.favoritePanel.bounds.y) - 4); + else region.getBounds().setBounds(this.fullBounds); + region.render(matrices, mouseX, mouseY, delta); renderAddFavorite(matrices, mouseX, mouseY, delta); } @@ -330,28 +193,10 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt } @Override - public boolean mouseDragged(double mouseX, double mouseY, int int_1, double double_3, double double_4) { - if (scrolling.mouseDragged(mouseX, mouseY, int_1, double_3, double_4, ConfigObject.getInstance().doesSnapToRows(), entrySize())) - return true; - return super.mouseDragged(mouseX, mouseY, int_1, double_3, double_4); - } - - private void updatePosition(float delta) { - if (ConfigObject.getInstance().doesSnapToRows() && scrolling.scrollTarget >= 0 && scrolling.scrollTarget <= scrolling.getMaxScroll()) { - double nearestRow = Math.round(scrolling.scrollTarget / (double) entrySize()) * (double) entrySize(); - if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(scrolling.scrollTarget, nearestRow, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON)) - scrolling.scrollTarget += (nearestRow - scrolling.scrollTarget) * Math.min(delta / 2.0, 1.0); - else - scrolling.scrollTarget = nearestRow; - } - scrolling.updatePosition(delta); - } - - @Override - public boolean keyPressed(int int_1, int int_2, int int_3) { + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (containsMouse(PointHelper.ofMouse())) for (Widget widget : children()) - if (widget.keyPressed(int_1, int_2, int_3)) + if (widget.keyPressed(keyCode, scanCode, modifiers)) return true; return false; } @@ -362,88 +207,8 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt public void updateSearch() { if (ConfigObject.getInstance().isFavoritesEnabled()) { - applyNewFavorites(CollectionUtils.map(ConfigObject.getInstance().getFavoriteEntries(), FavoriteEntry::copy)); - } else applyNewFavorites(Collections.emptyList()); - } - - public void applyNewFavorites(List newFavorites) { - newFavorites = Lists.newArrayList(newFavorites); - newFavorites.removeIf(FavoriteEntry::isEntryInvalid); - - int entrySize = entrySize(); - IntSet newFavoritesHash = new IntOpenHashSet(CollectionUtils.mapToInt(newFavorites, FavoriteEntry::hashCode)); - List removedEntries = Lists.newArrayList(this.entries.values()); - removedEntries.removeIf(entry -> newFavoritesHash.contains(entry.hashIgnoreAmount())); - - for (Entry removedEntry : removedEntries) { - removedEntry.remove(); - this.removedEntries.put(removedEntry.hashIgnoreAmount(), removedEntry); - } - - Int2ObjectMap prevEntries = new Int2ObjectOpenHashMap<>(entries); - this.entries.clear(); - - for (FavoriteEntry favorite : newFavorites) { - Entry entry = prevEntries.get(favorite.hashCode()); - - if (entry == null) { - entry = new Entry(favorite, entrySize); - } - - if (!ConfigObject.getInstance().isFavoritesAnimated()) entry.size.setAs(entrySize * 100); - else entry.size.setTo(entrySize * 100, 300); - entries.put(entry.hashIgnoreAmount(), entry); - } - - applyNewEntriesList(); - updateEntriesPosition(entry -> prevEntries.containsKey(entry.hashIgnoreAmount())); - } - - public void applyNewEntriesList() { - this.entriesList = Stream.concat(entries.values().stream().map(Entry::getWidget), removedEntries.values().stream().map(Entry::getWidget)).collect(Collectors.toList()); - this.children = Stream.>of( - entries.values().stream().map(Entry::getWidget), - removedEntries.values().stream().map(Entry::getWidget), - Stream.of(favoritePanelButton, favoritePanel) - ).flatMap(Function.identity()).collect(Collectors.toList()); - } - - public void updateEntriesPosition(Predicate animated) { - int entrySize = entrySize(); - this.blockedCount = 0; - if (favoritePanel.getBounds().height > 20) - this.currentBounds.setBounds(this.fullBounds.x, this.fullBounds.y, this.fullBounds.width, this.fullBounds.height - (this.fullBounds.getMaxY() - this.favoritePanel.bounds.y) - 4); - else this.currentBounds.setBounds(this.fullBounds); - this.innerBounds = updateInnerBounds(currentBounds); - int width = innerBounds.width / entrySize; - int currentX = 0; - int currentY = 0; - int releaseIndex = getReleaseIndex(); - - int slotIndex = 0; - for (Entry entry : this.entries.values()) { - while (true) { - int xPos = currentX * entrySize + innerBounds.x; - int yPos = currentY * entrySize + innerBounds.y; - - currentX++; - if (currentX >= width) { - currentX = 0; - currentY++; - } - - if (notSteppingOnExclusionZones(xPos, yPos - (int) scrolling.scrollAmount, entrySize, entrySize, innerBounds)) { - if (slotIndex++ == releaseIndex) { - continue; - } - - entry.moveTo(animated.test(entry), xPos, yPos); - break; - } else { - blockedCount++; - } - } - } + region.setEntries(CollectionUtils.map(ConfigObject.getInstance().getFavoriteEntries(), FavoriteEntry::copy)); + } else region.setEntries(Collections.emptyList()); } @Override @@ -451,123 +216,9 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt return children; } - public int getReleaseIndex() { - DraggingContext context = DraggingContext.getInstance(); - Point position = context.getCurrentPosition(); - if (context.isDraggingStack() && currentBounds.contains(position) && checkDraggedStacks(context.cast(), context.getCurrentStack()).isPresent()) { - int entrySize = entrySize(); - int width = innerBounds.width / entrySize; - int currentX = 0; - int currentY = 0; - List> entriesPoints = Lists.newArrayList(); - for (Entry entry : this.entries.values()) { - while (true) { - int xPos = currentX * entrySize + innerBounds.x; - int yPos = currentY * entrySize + innerBounds.y; - - currentX++; - if (currentX >= width) { - currentX = 0; - currentY++; - } - - if (notSteppingOnExclusionZones(xPos, yPos - (int) scrolling.scrollAmount, entrySize, entrySize, innerBounds)) { - entriesPoints.add(new Tuple<>(entry, new Point(xPos, yPos))); - break; - } else { - blockedCount++; - } - } - } - - int maxSize = entriesPoints.size(); - if (currentX != 0) { - int xPos = currentX * entrySize + innerBounds.x; - int yPos = currentY * entrySize + innerBounds.y; - - if (notSteppingOnExclusionZones(xPos, yPos - (int) scrolling.scrollAmount, entrySize, entrySize, innerBounds)) { - entriesPoints.add(new Tuple<>(null, new Point(xPos, yPos))); - } - } - - double x = position.x - 8; - double y = position.y + scrolling.scrollAmount - 8; - - return Mth.clamp(entriesPoints.stream() - .filter(value -> { - double otherY = value.getB().y; - - return otherY <= y + entrySize / 2 && otherY + entrySize > y + entrySize / 2; - }) - .min(Comparator.comparingDouble(value -> { - double otherX = value.getB().x; - double otherY = value.getB().y; - - return (x - otherX) * (x - otherX) + (y - otherY) * (y - otherY); - })) - .map(entriesPoints::indexOf) - .orElse(maxSize), - 0, entriesPoints.size()); - } - - return -2; - } - - public void drop(Entry entry, DraggableStack stack, FavoriteEntry favoriteEntry) { - DraggingContext context = DraggingContext.getInstance(); - double x = context.getCurrentPosition().x; - double y = context.getCurrentPosition().y + scrolling.scrollAmount; - entry.startedDraggingPosition = null; - - entry.x.setAs(x - 8); - entry.y.setAs(y - 8); - - boolean contains = currentBounds.contains(PointHelper.ofMouse()); - int newIndex = contains ? getReleaseIndex() : Math.max(0, Iterables.indexOf(entries.values(), e -> e == entry)); - - if (entries.size() - 1 <= newIndex) { - Entry remove = this.entries.remove(entry.hashIgnoreAmount()); - if (remove != null) { - remove.remove(); - this.removedEntries.put(remove.hashIgnoreAmount(), remove); - } - this.entries.put(entry.hashIgnoreAmount(), entry); - } else { - Int2ObjectMap prevEntries = new Int2ObjectLinkedOpenHashMap<>(entries); - this.entries.clear(); - - int index = 0; - for (Int2ObjectMap.Entry entryEntry : prevEntries.int2ObjectEntrySet()) { - if (index == newIndex) { - this.entries.put(entry.hashIgnoreAmount(), entry); - } - if (entryEntry.getIntKey() != entry.hashIgnoreAmount()) { - this.entries.put(entryEntry.getIntKey(), entryEntry.getValue()); - index++; - } - } - } - - applyNewEntriesList(); - - if (ConfigObject.getInstance().isFavoritesEnabled()) { - List favorites = ConfigObject.getInstance().getFavoriteEntries(); - favorites.clear(); - for (Entry value : this.entries.values()) { - favorites.add(value.entry.copy()); - } - - ConfigManager.getInstance().saveConfig(); - } - - applyNewFavorites(this.entries.values().stream() - .map(Entry::getEntry) - .collect(Collectors.toList())); - } - @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (scrolling.updateDraggingState(mouseX, mouseY, button)) + if (region.mouseClicked(mouseX, mouseY, button)) return true; for (Widget widget : children()) if (widget.mouseClicked(mouseX, mouseY, button)) @@ -585,163 +236,6 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableSt return false; } - public class Entry { - private FavoriteEntry entry; - private final EntryListEntry widget; - private boolean hidden; - private Point startedDraggingPosition; - private Animator x = new Animator(); - private Animator y = new Animator(); - private Animator size = new Animator(); - - public Entry(FavoriteEntry entry, int entrySize) { - this.entry = entry; - this.widget = (EntryListEntry) new EntryListEntry(this, 0, 0, entrySize, entry).noBackground(); - } - - public void remove() { - if (!hidden) { - this.hidden = true; - if (!ConfigObject.getInstance().isFavoritesAnimated()) this.size.setAs(0); - else this.size.setTo(0, 400); - } - } - - public void update(double delta) { - this.size.update(delta); - this.x.update(delta); - this.y.update(delta); - this.getWidget().getBounds().width = this.getWidget().getBounds().height = (int) Math.round(this.size.doubleValue() / 100); - double offsetSize = (entrySize() - this.size.doubleValue() / 100) / 2; - this.getWidget().getBounds().x = (int) Math.round(x.doubleValue() + offsetSize); - this.getWidget().getBounds().y = (int) Math.round(y.doubleValue() + offsetSize) - (int) scrolling.scrollAmount; - } - - public EntryListEntry getWidget() { - return widget; - } - - public boolean isHidden() { - return hidden; - } - - public int hashIgnoreAmount() { - return entry.hashCode(); - } - - public FavoriteEntry getEntry() { - return entry; - } - - public void moveTo(boolean animated, int xPos, int yPos) { - if (animated && ConfigObject.getInstance().isFavoritesAnimated()) { - x.setTo(xPos, 200); - y.setTo(yPos, 200); - } else { - x.setAs(xPos); - y.setAs(yPos); - } - } - } - - private class EntryListEntry extends EntryListEntryWidget { - private final Entry entry; - private final FavoriteEntry favoriteEntry; - - private EntryListEntry(Entry entry, int x, int y, int entrySize, FavoriteEntry favoriteEntry) { - super(new Point(x, y), entrySize); - this.entry = entry; - this.favoriteEntry = favoriteEntry; - this.clearEntries().entry(ClientEntryStacks.of(this.favoriteEntry.getRenderer(false))); - } - - @Override - protected FavoriteEntry asFavoriteEntry() { - return favoriteEntry.copy(); - } - - @Override - public boolean containsMouse(double mouseX, double mouseY) { - return super.containsMouse(mouseX, mouseY) && currentBounds.contains(mouseX, mouseY); - } - - @Override - protected boolean reverseFavoritesAction() { - return true; - } - - @Override - public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { - Optional overlayOptional = REIRuntime.getInstance().getOverlay(); - Optional>> menuEntries = favoriteEntry.getMenuEntries(); - if (Math.abs(entry.x.doubleValue() - entry.x.target()) < 1 && Math.abs(entry.y.doubleValue() - entry.y.target()) < 1 && overlayOptional.isPresent() && menuEntries.isPresent()) { - ScreenOverlayImpl overlay = (ScreenOverlayImpl) overlayOptional.get(); - UUID uuid = favoriteEntry.getUuid(); - - boolean isOpened = overlay.isMenuOpened(uuid); - if (isOpened || !overlay.isAnyMenuOpened()) { - boolean inBounds = containsMouse(mouseX, mouseY) || overlay.isMenuInBounds(uuid); - if (isOpened != inBounds) { - if (inBounds) { - Menu menu = new Menu(new Point(getBounds().x, getBounds().getMaxY()), - CollectionUtils.map(menuEntries.get().get(), entry -> new MenuEntry() { - @Override - public List children() { - return Collections.singletonList(entry); - } - - @Override - public void render(PoseStack poseStack, int i, int j, float f) { - entry.render(poseStack, i, j, f); - } - - @Override - public int getEntryWidth() { - return entry.getEntryWidth(); - } - - @Override - public int getEntryHeight() { - return entry.getEntryHeight(); - } - - @Override - public void updateInformation(int xPos, int yPos, boolean selected, boolean containsMouse, boolean rendering, int width) { - entry.closeMenu = overlay::closeOverlayMenu; - entry.updateInformation(xPos, yPos, selected, containsMouse, rendering, width); - } - - @Override - public int getZ() { - return entry.getZ(); - } - - @Override - public void setZ(int z) { - entry.setZ(z); - } - })); - if (ConfigObject.getInstance().isLeftHandSidePanel()) { - menu.menuStartPoint.x -= menu.getBounds().width - getBounds().width; - } - overlay.openMenu(uuid, menu, this::containsMouse, point -> entries.containsKey(entry.hashIgnoreAmount()) && !removedEntries.containsKey(entry.hashIgnoreAmount())); - } else { - overlay.closeOverlayMenu(); - } - } - } - } - Vector4f vector4f = new Vector4f(mouseX, mouseY, 0, 1.0F); - vector4f.transform(matrices.last().pose()); - super.render(matrices, (int) vector4f.x(), (int) vector4f.y(), delta); - } - - @Override - protected boolean doAction(double mouseX, double mouseY, int button) { - return favoriteEntry.doAction(button); - } - } - public static class ToggleAddFavoritePanelButton extends FadingFavoritePanelButton { public ToggleAddFavoritePanelButton(FavoritesListWidget widget) { super(widget); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RealRegionEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RealRegionEntry.java new file mode 100644 index 000000000..d0925f273 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RealRegionEntry.java @@ -0,0 +1,93 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021 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.region; + +import me.shedaniel.math.Point; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.entry.region.RegionEntry; +import me.shedaniel.rei.api.common.util.Animator; +import me.shed