aboutsummaryrefslogtreecommitdiff
path: root/runtime/src/main/java
diff options
context:
space:
mode:
authorshedaniel <daniel@shedaniel.me>2021-11-07 18:01:38 +0800
committershedaniel <daniel@shedaniel.me>2021-11-07 18:01:38 +0800
commit7879ac1cc61876bfa0578e34a96239830530a00b (patch)
treef8901af1f4ed947ead3f9beb60617aef11c26960 /runtime/src/main/java
parentf8fc41207ca444c0a90ae10d78a821831b245bc2 (diff)
downloadRoughlyEnoughItems-7879ac1cc61876bfa0578e34a96239830530a00b.tar.gz
RoughlyEnoughItems-7879ac1cc61876bfa0578e34a96239830530a00b.tar.bz2
RoughlyEnoughItems-7879ac1cc61876bfa0578e34a96239830530a00b.zip
Modularize region rendering
Diffstat (limited to 'runtime/src/main/java')
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java1
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java6
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java2
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java2
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java451
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/FavoritesListWidget.java624
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RealRegionEntry.java93
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionDraggableStack.java71
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionEntryListEntry.java143
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionListener.java41
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/registry/screen/ScreenRegistryImpl.java1
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/entry/comparison/EntryComparatorRegistryImpl.java8
12 files changed, 869 insertions, 574 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java
index 98f5aab24..2069d8139 100644
--- a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java
+++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java
@@ -283,6 +283,7 @@ public class RoughlyEnoughItemsCoreClient {
reloadPlugins(endReload, Platform.isFabric() ? ReloadStage.END : null);
});
ClientGuiEvent.INIT_POST.register((screen, access) -> {
+ REIRuntime.getInstance().getOverlay(false, true);
REIRuntimeImpl.getInstance().setPreviousScreen(screen);
if (ConfigObject.getInstance().doesDisableRecipeBook() && screen instanceof AbstractContainerScreen) {
access.getRenderables().removeIf(widget -> widget instanceof ImageButton button && button.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 e80e5f572..b674d0099 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<ScreenOverlay> getOverlay(boolean reset) {
- if (overlay == null || reset) {
+ public Optional<ScreenOverlay> 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 EventResult.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 0bc72e96f..f992145bc 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<T extends RegionEntry<T>> extends WidgetWithBounds implements DraggableStackVisitorWidget {
+ private final RegionListener<T> 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<RealRegionEntry<T>> entries = new Int2ObjectLinkedOpenHashMap<>();
+ private final Int2ObjectMap<RealRegionEntry<T>> removedEntries = new Int2ObjectLinkedOpenHashMap<>();
+ private List<RegionEntryListEntry<T>> entriesList = Lists.newArrayList();
+ private List<Widget> children = Lists.newArrayList();
+
+ public EntryStacksRegionWidget(RegionListener<T> 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<T> entry : entries.values()) {
+ entry.update(delta);
+ }
+ ObjectIterator<RealRegionEntry<T>> removedEntriesIterator = removedEntries.values().iterator();
+ while (removedEntriesIterator.hasNext()) {
+ RealRegionEntry<T> 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<RegionEntryListEntry<T>> 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<Widget> 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<Screen> context, double mouseX, double mouseY) {
+ if (innerBounds.contains(mouseX, mouseY)) {
+ for (RealRegionEntry<T> 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<T> entry : entries.values()) {
+ if (entry.getWidget().containsMouse(mouse)) {
+ return entry.getWidget().getCurrentEntry().copy();
+ }
+ }
+ }
+ return EntryStack.empty();
+ }
+
+ public Stream<EntryStack<?>> getEntries() {
+ return (Stream<EntryStack<?>>) (Stream<? extends EntryStack<?>>) 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<Screen> context, DraggableStack stack) {
+ return checkDraggedStacks(context, stack)
+ .filter(entry -> innerBounds.contains(context.getCurrentPosition()))
+ .map(entry -> {
+ drop(entry);
+ return Unit.INSTANCE;
+ }).isPresent();
+ }
+
+ public Optional<RealRegionEntry<T>> checkDraggedStacks(DraggingContext<Screen> context, DraggableStack stack) {
+ EntrySerializer<?> serializer = stack.getStack().getDefinition().getSerializer();
+ if (stack instanceof RegionDraggableStack || (serializer.supportReading() && serializer.supportSaving())) {
+ try {
+ T regionEntry = stack instanceof RegionDraggableStack ? ((RegionDraggableStack<T>) stack).getEntry().getEntry().copy()
+ : listener.convertDraggableStack(context, stack);
+ if (regionEntry == null) return Optional.empty();
+ RealRegionEntry<T> 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<T> 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<RealRegionEntry<T>> removedEntries = Lists.newArrayList(this.entries.values());
+ removedEntries.removeIf(entry -> newFavoritesHash.contains(entry.hashIgnoreAmount()));
+
+ for (RealRegionEntry<T> removedEntry : removedEntries) {
+ removedEntry.remove();
+ this.removedEntries.put(removedEntry.hashIgnoreAmount(), removedEntry);
+ }
+
+ Int2ObjectMap<RealRegionEntry<T>> prevEntries = new Int2ObjectOpenHashMap<>(entries);
+ this.entries.clear();
+
+ for (T regionEntry : newEntries) {
+ RealRegionEntry<T> 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.<Stream<Widget>>of(
+ entries.values().stream().map(RealRegionEntry::getWidget),
+ removedEntries.values().stream().map(RealRegionEntry::getWidget)
+ ).flatMap(Function.identity()).collect(Collectors.toList());
+ }
+
+ public void updateEntriesPosition(Predicate<RealRegionEntry<T>> 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<T> 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<Tuple<RealRegionEntry<T>, Point>> entriesPoints = Lists.newArrayList();
+ for (RealRegionEntry<T> 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<T> 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<T> 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<RealRegionEntry<T>> prevEntries = new Int2ObjectLinkedOpenHashMap<>(entries);
+ this.entries.clear();
+
+ int index = 0;
+ for (Int2ObjectMap.Entry<RealRegionEntry<T>> 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<T> entry) {
+ entries.remove(entry.hashIgnoreAmount());
+ setEntries(CollectionUtils.map(entries.values(), RealRegionEntry::getEntry));
+ }
+
+ public double getScrollAmount() {
+ return scrolling.scrollAmount;
+ }
+
+ public boolean has(RealRegionEntry<T> 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 5041887a4..c98d689b3 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<Entry> entries = new Int2ObjectLinkedOpenHashMap<>();
- private final Int2ObjectMap<Entry> removedEntries = new Int2ObjectLinkedOpenHashMap<>();
- private List<EntryListEntry> entriesList = Lists.newArrayList();
- private List<Widget> children = Lists.newArrayList();
+public class FavoritesListWidget extends WidgetWithBounds implements DraggableStackProviderWidget, OverlayListWidget, RegionListener<FavoriteEntry> {
+ private Rectangle fullBounds;
+ private EntryStacksRegionWidget<FavoriteEntry> 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<Widget> 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<FavoriteEntry> getRegio