aboutsummaryrefslogtreecommitdiff
path: root/default-plugin/src/main/java/me
diff options
context:
space:
mode:
Diffstat (limited to 'default-plugin/src/main/java/me')
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java60
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/DefaultInformationCategory.java21
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconBaseCategory.java31
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconPaymentCategory.java31
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/DefaultTagCategory.java204
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/ReferenceTagNodeWidget.java83
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/TagNodeWidget.java43
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/TagTreeWidget.java122
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/TagTreesWidget.java48
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/ValueTagNodeWidget.java105
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/BuiltinPlugin.java3
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/DefaultInformationDisplay.java2
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/brewing/DefaultBrewingDisplay.java6
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/tag/DefaultTagDisplay.java102
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/tag/TagNode.java141
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/tag/TagNodes.java211
16 files changed, 1138 insertions, 75 deletions
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java
index 729f48cc1..7f1a2926d 100644
--- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java
@@ -43,7 +43,7 @@ import me.shedaniel.rei.api.client.registry.screen.ExclusionZones;
import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
import me.shedaniel.rei.api.common.entry.EntryIngredient;
-import me.shedaniel.rei.api.common.util.CollectionUtils;
+import me.shedaniel.rei.api.common.entry.EntryStack;
import me.shedaniel.rei.api.common.util.EntryIngredients;
import me.shedaniel.rei.api.common.util.EntryStacks;
import me.shedaniel.rei.impl.ClientInternals;
@@ -54,6 +54,7 @@ import me.shedaniel.rei.plugin.client.categories.beacon.DefaultBeaconBaseCategor
import me.shedaniel.rei.plugin.client.categories.beacon.DefaultBeaconPaymentCategory;
import me.shedaniel.rei.plugin.client.categories.cooking.DefaultCookingCategory;
import me.shedaniel.rei.plugin.client.categories.crafting.DefaultCraftingCategory;
+import me.shedaniel.rei.plugin.client.categories.tag.DefaultTagCategory;
import me.shedaniel.rei.plugin.client.exclusionzones.DefaultPotionEffectExclusionZones;
import me.shedaniel.rei.plugin.client.exclusionzones.DefaultRecipeBookExclusionZones;
import me.shedaniel.rei.plugin.client.favorites.GameModeFavoriteEntry;
@@ -71,16 +72,14 @@ import me.shedaniel.rei.plugin.common.displays.cooking.DefaultSmeltingDisplay;
import me.shedaniel.rei.plugin.common.displays.cooking.DefaultSmokingDisplay;
import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay;
import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomDisplay;
+import me.shedaniel.rei.plugin.common.displays.tag.DefaultTagDisplay;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screens.inventory.*;
import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener;
-import net.minecraft.core.Holder;
-import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
-import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
@@ -163,6 +162,7 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
new DefaultWaxScrapingCategory(),
new DefaultOxidizingCategory(),
new DefaultOxidationScrapingCategory(),
+ new DefaultTagCategory(),
new DefaultInformationCategory()
);
@@ -195,32 +195,23 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
registry.addWorkstations(PATHING, EntryStacks.of(item));
}
});
- for (Item item : getTag(new ResourceLocation("c", "axes"))) {
- if (axes.add(item)) {
- registry.addWorkstations(STRIPPING, EntryStacks.of(item));
- registry.addWorkstations(WAX_SCRAPING, EntryStacks.of(item));
- registry.addWorkstations(OXIDATION_SCRAPING, EntryStacks.of(item));
+ for (EntryStack<?> stack : getTag(new ResourceLocation("c", "axes"))) {
+ if (axes.add(stack.<ItemStack>castValue().getItem())) {
+ registry.addWorkstations(STRIPPING, stack);
+ registry.addWorkstations(WAX_SCRAPING, stack);
+ registry.addWorkstations(OXIDATION_SCRAPING, stack);
}
}
- for (Item item : getTag(new ResourceLocation("c", "hoes"))) {
- if (hoes.add(item)) registry.addWorkstations(TILLING, EntryStacks.of(item));
+ for (EntryStack<?> stack : getTag(new ResourceLocation("c", "hoes"))) {
+ if (hoes.add(stack.<ItemStack>castValue().getItem())) registry.addWorkstations(TILLING, stack);
}
- for (Item item : getTag(new ResourceLocation("c", "shovels"))) {
- if (shovels.add(item)) registry.addWorkstations(PATHING, EntryStacks.of(item));
+ for (EntryStack<?> stack : getTag(new ResourceLocation("c", "shovels"))) {
+ if (shovels.add(stack.<ItemStack>castValue().getItem())) registry.addWorkstations(PATHING, stack);
}
}
- private static <T> Iterable<T> resolveTag(TagKey<T> tagKey) {
- Registry<T> registry = ((Registry<Registry<T>>) Registry.REGISTRY).get((ResourceKey<Registry<T>>) tagKey.registry());
- HolderSet.Named<T> holders = registry.getTag(tagKey).orElse(null);
- if (holders == null) return Collections.emptyList();
- return () -> holders.stream()
- .map(Holder::value)
- .iterator();
- }
-
- private static Iterable<Item> getTag(ResourceLocation tagId) {
- return resolveTag(TagKey.create(Registry.ITEM_REGISTRY, tagId));
+ private static EntryIngredient getTag(ResourceLocation tagId) {
+ return EntryIngredients.ofItemTag(TagKey.create(Registry.ITEM_REGISTRY, tagId));
}
@Override
@@ -234,6 +225,17 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
registry.registerRecipeFiller(UpgradeRecipe.class, RecipeType.SMITHING, DefaultSmithingDisplay::new);
registry.registerFiller(AnvilRecipe.class, DefaultAnvilDisplay::new);
registry.registerFiller(BrewingRecipe.class, DefaultBrewingDisplay::new);
+ registry.registerFiller(TagKey.class, tagKey -> {
+ if (tagKey.isFor(Registry.ITEM_REGISTRY)) {
+ return DefaultTagDisplay.ofItems(tagKey);
+ } else if (tagKey.isFor(Registry.BLOCK_REGISTRY)) {
+ return DefaultTagDisplay.ofItems(tagKey);
+ } else if (tagKey.isFor(Registry.FLUID_REGISTRY)) {
+ return DefaultTagDisplay.ofFluids(tagKey);
+ }
+
+ return null;
+ });
for (Map.Entry<Item, Integer> entry : AbstractFurnaceBlockEntity.getFuel().entrySet()) {
registry.add(new DefaultFuelDisplay(Collections.singletonList(EntryIngredients.of(entry.getKey())), Collections.emptyList(), entry.getValue()));
}
@@ -271,8 +273,8 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
DummyShovelItem.getPathBlocksMap().entrySet().stream().sorted(Comparator.comparing(b -> Registry.BLOCK.getKey(b.getKey()))).forEach(set -> {
registry.add(new DefaultPathingDisplay(EntryStacks.of(set.getKey()), EntryStacks.of(set.getValue().getBlock())));
});
- registry.add(new DefaultBeaconBaseDisplay(CollectionUtils.map(resolveTag(BlockTags.BEACON_BASE_BLOCKS), ItemStack::new)));
- registry.add(new DefaultBeaconPaymentDisplay(CollectionUtils.map(resolveTag(ItemTags.BEACON_PAYMENT_ITEMS), ItemStack::new)));
+ registry.add(new DefaultBeaconBaseDisplay(Collections.singletonList(EntryIngredients.ofItemTag(BlockTags.BEACON_BASE_BLOCKS)), Collections.emptyList()));
+ registry.add(new DefaultBeaconPaymentDisplay(Collections.singletonList(EntryIngredients.ofItemTag(ItemTags.BEACON_PAYMENT_ITEMS)), Collections.emptyList()));
HoneycombItem.WAXABLES.get().entrySet().stream().sorted(Comparator.comparing(b -> Registry.BLOCK.getKey(b.getKey()))).forEach(set -> {
registry.add(new DefaultWaxingDisplay(EntryStacks.of(set.getKey()), EntryStacks.of(set.getValue())));
});
@@ -317,6 +319,10 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
} else {
registerForgePotions(registry, this);
}
+
+ for (Registry<?> reg : Registry.REGISTRY) {
+ reg.getTags().forEach(tagPair -> registry.add(tagPair.getFirst()));
+ }
}
@ExpectPlatform
@@ -350,7 +356,7 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
public void registerFavorites(FavoriteEntryType.Registry registry) {
registry.register(GameModeFavoriteEntry.ID, GameModeFavoriteEntry.Type.INSTANCE);
registry.getOrCrateSection(new TranslatableComponent(GameModeFavoriteEntry.TRANSLATION_KEY))
- .add(true, Stream.concat(
+ .add(Stream.concat(
Arrays.stream(GameType.values())
.filter(type -> type.getId() >= 0),
Stream.of((GameType) null)
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/DefaultInformationCategory.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/DefaultInformationCategory.java
index 5cb04c7cd..0f7baf641 100644
--- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/DefaultInformationCategory.java
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/DefaultInformationCategory.java
@@ -36,6 +36,7 @@ import me.shedaniel.rei.api.client.REIRuntime;
import me.shedaniel.rei.api.client.gui.AbstractRenderer;
import me.shedaniel.rei.api.client.gui.DisplayRenderer;
import me.shedaniel.rei.api.client.gui.Renderer;
+import me.shedaniel.rei.api.client.gui.widgets.CloseableScissors;
import me.shedaniel.rei.api.client.gui.widgets.Widget;
import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
import me.shedaniel.rei.api.client.gui.widgets.Widgets;
@@ -194,18 +195,18 @@ public class DefaultInformationCategory implements DisplayCategory<DefaultInform
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
scrolling.updatePosition(delta);
Rectangle innerBounds = scrolling.getScissorBounds();
- ScissorsHandler.INSTANCE.scissor(innerBounds);
- int currentY = -scrolling.scrollAmountInt() + innerBounds.y;
- for (FormattedCharSequence text : texts) {
- if (text != null && currentY + font.lineHeight >= innerBounds.y && currentY <= innerBounds.getMaxY()) {
- font.draw(matrices, text, innerBounds.x + 2, currentY + 2, REIRuntime.getInstance().isDarkThemeEnabled() ? 0xFFBBBBBB : 0xFF090909);
+ try (CloseableScissors scissors = scissor(matrices, innerBounds)) {
+ int currentY = -scrolling.scrollAmountInt() + innerBounds.y;
+ for (FormattedCharSequence text : texts) {
+ if (text != null && currentY + font.lineHeight >= innerBounds.y && currentY <= innerBounds.getMaxY()) {
+ font.draw(matrices, text, innerBounds.x + 2, currentY + 2, REIRuntime.getInstance().isDarkThemeEnabled() ? 0xFFBBBBBB : 0xFF090909);
+ }
+ currentY += text == null ? 4 : font.lineHeight;
}
- currentY += text == null ? 4 : font.lineHeight;
}
- ScissorsHandler.INSTANCE.removeLastScissor();
- ScissorsHandler.INSTANCE.scissor(scrolling.getBounds());
- scrolling.renderScrollBar(0xff000000, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f);
- ScissorsHandler.INSTANCE.removeLastScissor();
+ try (CloseableScissors scissors = scissor(matrices, scrolling.getBounds())) {
+ scrolling.renderScrollBar(0xff000000, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f);
+ }
}
@Override
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconBaseCategory.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconBaseCategory.java
index d2dd424cf..645457e13 100644
--- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconBaseCategory.java
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconBaseCategory.java
@@ -33,10 +33,7 @@ import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.REIRuntime;
import me.shedaniel.rei.api.client.gui.DisplayRenderer;
import me.shedaniel.rei.api.client.gui.Renderer;
-import me.shedaniel.rei.api.client.gui.widgets.Slot;
-import me.shedaniel.rei.api.client.gui.widgets.Widget;
-import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
-import me.shedaniel.rei.api.client.gui.widgets.Widgets;
+import me.shedaniel.rei.api.client.gui.widgets.*;
import me.shedaniel.rei.api.client.registry.display.DisplayCategory;
import me.shedaniel.rei.api.common.category.CategoryIdentifier;
import me.shedaniel.rei.api.common.util.CollectionUtils;
@@ -158,21 +155,21 @@ public class DefaultBeaconBaseCategory implements DisplayCategory<DefaultBeaconB
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
scrolling.updatePosition(delta);
Rectangle innerBounds = scrolling.getScissorBounds();
- ScissorsHandler.INSTANCE.scissor(innerBounds);
- for (int y = 0; y < Mth.ceil(widgets.size() / 8f); y++) {
- for (int x = 0; x < 8; x++) {
- int index = y * 8 + x;
- if (widgets.size() <= index)
- break;
- Slot widget = widgets.get(index);
- widget.getBounds().setLocation(bounds.x + 1 + x * 18, bounds.y + 1 + y * 18 - scrolling.scrollAmountInt());
- widget.render(matrices, mouseX, mouseY, delta);
+ try (CloseableScissors scissors = scissor(matrices, innerBounds)) {
+ for (int y = 0; y < Mth.ceil(widgets.size() / 8f); y++) {
+ for (int x = 0; x < 8; x++) {
+ int index = y * 8 + x;
+ if (widgets.size() <= index)
+ break;
+ Slot widget = widgets.get(index);
+ widget.getBounds().setLocation(bounds.x + 1 + x * 18, bounds.y + 1 + y * 18 - scrolling.scrollAmountInt());
+ widget.render(matrices, mouseX, mouseY, delta);
+ }
}
}
- ScissorsHandler.INSTANCE.removeLastScissor();
- ScissorsHandler.INSTANCE.scissor(scrolling.getBounds());
- scrolling.renderScrollBar(0xff000000, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f);
- ScissorsHandler.INSTANCE.removeLastScissor();
+ try (CloseableScissors scissors = scissor(matrices, scrolling.getBounds())) {
+ scrolling.renderScrollBar(0xff000000, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f);
+ }
}
@Override
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconPaymentCategory.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconPaymentCategory.java
index 71642fc1e..2b7f69b97 100644
--- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconPaymentCategory.java
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/beacon/DefaultBeaconPaymentCategory.java
@@ -33,10 +33,7 @@ import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.REIRuntime;
import me.shedaniel.rei.api.client.gui.DisplayRenderer;
import me.shedaniel.rei.api.client.gui.Renderer;
-import me.shedaniel.rei.api.client.gui.widgets.Slot;
-import me.shedaniel.rei.api.client.gui.widgets.Widget;
-import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds;
-import me.shedaniel.rei.api.client.gui.widgets.Widgets;
+import me.shedaniel.rei.api.client.gui.widgets.*;
import me.shedaniel.rei.api.client.registry.display.DisplayCategory;
import me.shedaniel.rei.api.common.category.CategoryIdentifier;
import me.shedaniel.rei.api.common.util.CollectionUtils;
@@ -158,21 +155,21 @@ public class DefaultBeaconPaymentCategory implements DisplayCategory<DefaultBeac
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
scrolling.updatePosition(delta);
Rectangle innerBounds = scrolling.getScissorBounds();
- ScissorsHandler.INSTANCE.scissor(innerBounds);
- for (int y = 0; y < Mth.ceil(widgets.size() / 8f); y++) {
- for (int x = 0; x < 8; x++) {
- int index = y * 8 + x;
- if (widgets.size() <= index)
- break;
- Slot widget = widgets.get(index);
- widget.getBounds().setLocation(bounds.x + 1 + x * 18, bounds.y + 1 + y * 18 - scrolling.scrollAmountInt());
- widget.render(matrices, mouseX, mouseY, delta);
+ try (CloseableScissors scissors = scissor(matrices, innerBounds)) {
+ for (int y = 0; y < Mth.ceil(widgets.size() / 8f); y++) {
+ for (int x = 0; x < 8; x++) {
+ int index = y * 8 + x;
+ if (widgets.size() <= index)
+ break;
+ Slot widget = widgets.get(index);
+ widget.getBounds().setLocation(bounds.x + 1 + x * 18, bounds.y + 1 + y * 18 - scrolling.scrollAmountInt());
+ widget.render(matrices, mouseX, mouseY, delta);
+ }
}
}
- ScissorsHandler.INSTANCE.removeLastScissor();
- ScissorsHandler.INSTANCE.scissor(scrolling.getBounds());
- scrolling.renderScrollBar(0xff000000, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f);
- ScissorsHandler.INSTANCE.removeLastScissor();
+ try (CloseableScissors scissors = scissor(matrices, scrolling.getBounds())) {
+ scrolling.renderScrollBar(0xff000000, 1, REIRuntime.getInstance().isDarkThemeEnabled() ? 0.8f : 1f);
+ }
}
@Override
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/DefaultTagCategory.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/DefaultTagCategory.java
new file mode 100644
index 000000000..24296cfaa
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/DefaultTagCategory.java
@@ -0,0 +1,204 @@
+/*
+ * 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.plugin.client.categories.tag;
+
+import com.mojang.blaze3d.platform.Window;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.math.Matrix4f;
+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.ClientHelper;
+import me.shedaniel.rei.api.client.REIRuntime;
+import me.shedaniel.rei.api.client.gui.AbstractRenderer;
+import me.shedaniel.rei.api.client.gui.Renderer;
+import me.shedaniel.rei.api.client.gui.widgets.*;
+import me.shedaniel.rei.api.client.registry.display.DisplayCategory;
+import me.shedaniel.rei.api.client.util.ClientEntryStacks;
+import me.shedaniel.rei.api.common.category.CategoryIdentifier;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.CollectionUtils;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.shedaniel.rei.plugin.common.BuiltinPlugin;
+import me.shedaniel.rei.plugin.common.displays.tag.DefaultTagDisplay;
+import me.shedaniel.rei.plugin.common.displays.tag.TagNode;
+import me.shedaniel.rei.plugin.common.displays.tag.TagNodes;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Font;
+import net.minecraft.core.Holder;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TextComponent;
+import net.minecraft.network.chat.TranslatableComponent;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.Items;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+public class DefaultTagCategory implements DisplayCategory<DefaultTagDisplay<?, ?>> {
+ @Override
+ public CategoryIdentifier<? extends DefaultTagDisplay<?, ?>> getCategoryIdentifier() {
+ return BuiltinPlugin.TAG;
+ }
+
+ @Override
+ public Component getTitle() {
+ return new TranslatableComponent("category.rei.tag");
+ }
+
+ @Override
+ public Renderer getIcon() {
+ return EntryStacks.of(Items.NAME_TAG);
+ }
+
+ @Override
+ public List<Widget> setupDisplay(DefaultTagDisplay<?, ?> display, Rectangle bounds) {
+ List<Widget> widgets = new ArrayList<>();
+
+ Window window = Minecraft.getInstance().getWindow();
+ Rectangle boundsBig = new Rectangle(window.getGuiScaledWidth() * 0.05, window.getGuiScaledHeight() * 0.1, window.getGuiScaledWidth() * 0.9, window.getGuiScaledHeight() * 0.8);
+ Rectangle recipeBounds = bounds.clone();
+
+ boolean[] expanded = {false};
+
+ Rectangle innerBounds = new Rectangle(bounds.x + 6 + 14, bounds.y + 6, bounds.width - 12 - 14, bounds.height - 12);
+ Rectangle overflowBounds = new Rectangle(innerBounds.x + 1, innerBounds.y + 1, innerBounds.width - 2, innerBounds.height - 2);
+
+ Rectangle expandButtonBounds = new Rectangle(bounds.x + 5, bounds.y + 6, 13, 13);
+ Rectangle expandOverlayBounds = new Rectangle(bounds.x + 5 + 2, bounds.y + 6 + 2, 13 - 4, 13 - 4);
+ Rectangle copyButtonBounds = new Rectangle(bounds.x + 5, bounds.getMaxY() - 6 - 13, 13, 13);
+ Rectangle copyOverlayBounds = new Rectangle(bounds.x + 5 + 2, bounds.getMaxY() - 6 - 13 + 2, 13 - 4, 13 - 4);
+
+ ValueAnimator<FloatingRectangle> boundsAnimator = ValueAnimator.ofFloatingRectangle(bounds.getFloatingBounds())
+ .withConvention(() -> {
+ if (expanded[0]) {
+ return boundsBig.getFloatingBounds();
+ } else {
+ return bounds.getFloatingBounds();
+ }
+ }, 1400);
+
+ widgets.add(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> {
+ innerBounds.setBounds(recipeBounds.x + 6 + 14, recipeBounds.y + 6, recipeBounds.width - 12 - 14, recipeBounds.height - 12);
+ overflowBounds.setBounds(innerBounds.x + 1, innerBounds.y + 1, innerBounds.width - 2, innerBounds.height - 2);
+ expandButtonBounds.setBounds(recipeBounds.x + 5, recipeBounds.y + 6, 13, 13);
+ copyButtonBounds.setBounds(recipeBounds.x + 5, recipeBounds.getMaxY() - 6 - 13, 13, 13);
+ expandOverlayBounds.setBounds(recipeBounds.x + 5 + 2, recipeBounds.y + 6 + 2, 13 - 4, 13 - 4);
+ copyOverlayBounds.setBounds(recipeBounds.x + 5 + 2, recipeBounds.getMaxY() - 6 - 13 + 2, 13 - 4, 13 - 4);
+ recipeBounds.setBounds(boundsAnimator.value());
+ boundsAnimator.update(delta);
+
+ if (overflowBounds.contains(mouseX, mouseY)) {
+ REIRuntime.getInstance().clearTooltips();
+ }
+ }));
+
+ widgets.add(Widgets.createRecipeBase(recipeBounds));
+ widgets.add(Widgets.createSlotBase(innerBounds));
+
+ WidgetWithBounds[] delegate = new WidgetWithBounds[]{Widgets.noOp()};
+ TagNode<?>[] tagNode = new TagNode[]{null};
+ widgets.add(Widgets.withTranslate(Widgets.delegateWithBounds(() -> delegate[0]), 0, 0, 20));
+
+ TagNodes.create(display.getKey(), dataResult -> {
+ if (dataResult.error().isPresent()) {
+ delegate[0] = Widgets.withBounds(Widgets.concat(
+ Widgets.createLabel(new Point(innerBounds.getCenterX(), innerBounds.getCenterY() - 8), new TextComponent("Failed to resolve tags!")),
+ Widgets.createLabel(new Point(innerBounds.getCenterX(), innerBounds.getCenterY() - 8), new TextComponent(dataResult.error().get().message()))
+ ), overflowBounds);
+ } else {
+ tagNode[0] = dataResult.result().get();
+ //noinspection rawtypes
+ Function<? extends Holder<?>, ? extends EntryStack<?>> displayMapper = display.getMapper();
+ Function<Holder<?>, EntryStack<?>> mapper = holder -> {
+ EntryStack<?> stack = ((Function<Holder<?>, EntryStack<?>>) displayMapper).apply(holder);
+ if (stack.isEmpty()) {
+ return ClientEntryStacks.of(new AbstractRenderer() {
+ @Override
+ public void render(PoseStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
+ Minecraft instance = Minecraft.getInstance();
+ Font font = instance.font;
+ String text = "?";
+ int width = font.width(text);
+ font.draw(matrices, text, bounds.getCenterX() - width / 2f + 0.2f, bounds.getCenterY() - font.lineHeight / 2f + 1f, REIRuntime.getInstance().isDarkThemeEnabled() ? -4473925 : -12566464);
+ }
+
+ @Override
+ @Nullable
+ public Tooltip getTooltip(TooltipContext context) {
+ return Tooltip.create(context.getPoint(), new TextComponent(holder.unwrapKey().map(key -> key.location().toString()).orElse("null")));
+ }
+ });
+ }
+ return stack;
+ };
+ delegate[0] = Widgets.overflowed(overflowBounds, Widgets.padded(16, new TagTreeWidget(tagNode[0], mapper)));
+ }
+ });
+
+ widgets.add(Widgets.createButton(expandButtonBounds, new TextComponent(""))
+ .onRender((poseStack, button) -> {
+ button.setEnabled(tagNode[0] != null);
+ })
+ .onClick(button -> {
+ expanded[0] = !expanded[0];
+ })
+ .tooltipSupplier(button -> new Component[]{new TranslatableComponent(!expanded[0] ? "text.rei.expand.view" : "text.rei.expand.view.close")}));
+ widgets.add(Widgets.createButton(copyButtonBounds, new TextComponent(""))
+ .onRender((poseStack, button) -> {
+ button.setEnabled(tagNode[0] != null);
+ })
+ .onClick(button -> {
+ TagNode<?> node = tagNode[0];
+
+ if (node != null) {
+ Minecraft.getInstance().keyboardHandler.setClipboard(node.asTree());
+ }
+ })
+ .tooltipLine(new TranslatableComponent("text.rei.tag.copy.clipboard")));
+ widgets.add(Widgets.withTranslate(new DelegateWidget(Widgets.noOp()) {
+ @Override
+ protected Widget delegate() {
+ ResourceLocation expandTexture = !expanded[0] ? new ResourceLocation("roughlyenoughitems", "textures/gui/expand.png")
+ : new ResourceLocation("roughlyenoughitems", "textures/gui/shrink.png");
+ return Widgets.concat(
+ Widgets.createTexturedWidget(expandTexture,
+ new Rectangle(recipeBounds.x + 5 + 2, recipeBounds.y + 6 + 2, 13 - 4, 13 - 4), 0, 0, 9, 9),
+ Widgets.createTexturedWidget(new ResourceLocation("roughlyenoughitems", "textures/gui/clipboard.png"),
+ new Rectangle(recipeBounds.x + 5 + 2, recipeBounds.getMaxY() - 6 - 13 + 2, 13 - 4, 13 - 4), 0, 0, 9, 9)
+ );
+ }
+ }, 0, 0, 10));
+
+ Matrix4f translateMatrix = Matrix4f.createTranslateMatrix(0, 0, 200);
+ Matrix4f identity = new Matrix4f();
+ identity.setIdentity();
+ return CollectionUtils.map(widgets, widget -> Widgets.withTranslate(widget, () ->
+ expanded[0] || !boundsAnimator.value().equals(boundsAnimator.target()) ? translateMatrix : identity));
+ }
+}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/ReferenceTagNodeWidget.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/ReferenceTagNodeWidget.java
new file mode 100644
index 000000000..75beaf41c
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/tag/ReferenceTagNodeWidget.java
@@ -0,0 +1,83 @@
+/*
+ * 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.plugin.client.categories.tag;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.PoseStack;
+import me.shedaniel.math.Rectangle;
+import me.shedaniel.rei.api.client.gui.widgets.Slot;
+import me.shedaniel.rei.api.client.gui.widgets.Tooltip;
+import me.shedaniel.rei.api.client.gui.widgets.Widgets;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.EntryIngredients;
+import me.shedaniel.rei.plugin.common.displays.tag.TagNode;
+import net.minecraft.client.gui.components.events.GuiEventListener;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.core.Holder;
+import net.minecraft.network.chat.TextComponent;
+import net.minecraft.resources.ResourceLocation;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+public class ReferenceTagNodeWidget<S, T> extends TagNodeWidget<S, T> {
+ private final TagNode<S> node;
+ private final Rectangle bounds;
+ private final Slot slot;
+ private final List<? extends GuiEventListener> children;
+
+ public ReferenceTagNodeWidget(TagNode<S> node, Function<Holder<S>, EntryStack<T>> mapper) {
+ this.node = node;
+ this.bounds = new Rectangle(0, 0, 24, 23);
+ this.slot = Widgets.createSlot(new Rectangle(0, 0, 18, 18))
+ .disableBackground()
+ .disableHighlight()
+ .disableTooltips()
+ .entries(EntryIngredients.ofTag(node.getReference(), mapper));
+ this.children = Collections.singletonList(this.slot);
+ }
+
+ @Override
+ public Rectangle getBounds() {
+ return bounds;
+ }
+
+ @Override
+ public void render(PoseStack poses, int mouseX, int mouseY, float delta) {
+ RenderSystem.setShader(GameRenderer::getPositionTexShader);
+ RenderSystem.setShaderTexture(0, new ResourceLocation("textures/gui/advancements/widgets.png"));
+ this.blit(poses, bounds.x, bounds.y, 1, 128 + 27, 24, 24);
+ this.slot.getBounds().setLocation(bounds.getCenterX() - this.slot.getBo