From ddbedf6a7dc89176ab7797e5aaa10d6ea563f9a9 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 8 Jul 2020 22:49:27 +0800 Subject: Custom Filtering Rules Signed-off-by: shedaniel --- .../java/me/shedaniel/rei/api/ConfigObject.java | 3 + .../java/me/shedaniel/rei/api/EntryRegistry.java | 3 + .../gui/config/entry/FilteringAddRuleScreen.java | 194 ++++++++++++++++ .../rei/gui/config/entry/FilteringEntry.java | 16 +- .../config/entry/FilteringRuleOptionsScreen.java | 239 ++++++++++++++++++++ .../rei/gui/config/entry/FilteringRulesScreen.java | 246 +++++++++++++++++++++ .../rei/gui/config/entry/FilteringScreen.java | 24 +- .../rei/gui/config/entry/NoFilteringEntry.java | 4 +- .../me/shedaniel/rei/impl/ConfigManagerImpl.java | 19 +- .../me/shedaniel/rei/impl/ConfigObjectImpl.java | 11 +- .../me/shedaniel/rei/impl/EntryRegistryImpl.java | 14 +- .../java/me/shedaniel/rei/impl/SearchArgument.java | 11 +- .../rei/impl/filtering/AbstractFilteringRule.java | 40 ++++ .../rei/impl/filtering/FilteringContext.java | 52 +++++ .../rei/impl/filtering/FilteringContextImpl.java | 63 ++++++ .../rei/impl/filtering/FilteringContextType.java | 34 +++ .../rei/impl/filtering/FilteringResult.java | 67 ++++++ .../rei/impl/filtering/FilteringResultImpl.java | 52 +++++ .../rei/impl/filtering/FilteringRule.java | 92 ++++++++ .../impl/filtering/rules/ManualFilteringRule.java | 92 ++++++++ .../impl/filtering/rules/SearchFilteringRule.java | 172 ++++++++++++++ .../me/shedaniel/rei/impl/search/Argument.java | 2 +- .../rei/impl/search/ArgumentsRegistry.java | 22 +- 23 files changed, 1445 insertions(+), 27 deletions(-) create mode 100644 src/main/java/me/shedaniel/rei/gui/config/entry/FilteringAddRuleScreen.java create mode 100644 src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRuleOptionsScreen.java create mode 100644 src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRulesScreen.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/AbstractFilteringRule.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/FilteringContext.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextImpl.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextType.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/FilteringResult.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/FilteringResultImpl.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/FilteringRule.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/rules/ManualFilteringRule.java create mode 100644 src/main/java/me/shedaniel/rei/impl/filtering/rules/SearchFilteringRule.java (limited to 'src/main/java/me') diff --git a/src/main/java/me/shedaniel/rei/api/ConfigObject.java b/src/main/java/me/shedaniel/rei/api/ConfigObject.java index a581ce649..4f4a3ff3f 100644 --- a/src/main/java/me/shedaniel/rei/api/ConfigObject.java +++ b/src/main/java/me/shedaniel/rei/api/ConfigObject.java @@ -26,6 +26,7 @@ package me.shedaniel.rei.api; import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.rei.gui.config.*; import me.shedaniel.rei.impl.ConfigManagerImpl; +import me.shedaniel.rei.impl.filtering.FilteringRule; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import org.jetbrains.annotations.ApiStatus; @@ -150,6 +151,8 @@ public interface ConfigObject { List getFilteredStacks(); + List> getFilteringRules(); + @ApiStatus.Experimental boolean shouldAsyncSearch(); diff --git a/src/main/java/me/shedaniel/rei/api/EntryRegistry.java b/src/main/java/me/shedaniel/rei/api/EntryRegistry.java index 80f25cbf6..56c9aac19 100644 --- a/src/main/java/me/shedaniel/rei/api/EntryRegistry.java +++ b/src/main/java/me/shedaniel/rei/api/EntryRegistry.java @@ -25,6 +25,8 @@ package me.shedaniel.rei.api; import me.shedaniel.rei.RoughlyEnoughItemsCore; import me.shedaniel.rei.utils.CollectionUtils; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import org.jetbrains.annotations.ApiStatus; @@ -33,6 +35,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +@Environment(EnvType.CLIENT) public interface EntryRegistry { /** diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringAddRuleScreen.java b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringAddRuleScreen.java new file mode 100644 index 000000000..cfcb02396 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringAddRuleScreen.java @@ -0,0 +1,194 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.gui.config.entry; + +import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget; +import me.shedaniel.rei.impl.filtering.FilteringRule; +import me.shedaniel.rei.impl.filtering.rules.ManualFilteringRule; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.LiteralText; +import net.minecraft.text.StringRenderable; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Identifier; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; + +public class FilteringAddRuleScreen extends Screen { + private final FilteringEntry entry; + private RulesList rulesList; + Screen parent; + + public FilteringAddRuleScreen(FilteringEntry entry) { + super(new TranslatableText("config.roughlyenoughitems.filteringRulesScreen.new")); + this.entry = entry; + } + + @Override + protected void init() { + super.init(); + { + Text backText = new LiteralText("↩ ").append(new TranslatableText("gui.back")); + addButton(new ButtonWidget(4, 4, MinecraftClient.getInstance().textRenderer.getWidth(backText) + 10, 20, backText, button -> { + client.openScreen(parent); + this.parent = null; + })); + } + rulesList = addChild(new RulesList(client, width, height, 30, height, BACKGROUND_TEXTURE)); + for (FilteringRule rule : FilteringRule.REGISTRY) { + if (!(rule instanceof ManualFilteringRule)) + rulesList.addItem(new DefaultRuleEntry(parent, entry, rule.createNew(), null)); + } + rulesList.selectItem(rulesList.children().get(0)); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.rulesList.render(matrices, mouseX, mouseY, delta); + super.render(matrices, mouseX, mouseY, delta); + this.textRenderer.drawWithShadow(matrices, this.title, this.width / 2.0F - this.textRenderer.getWidth(this.title) / 2.0F, 12.0F, -1); + } + + public static class RulesList extends DynamicElementListWidget { + private boolean inFocus; + + public RulesList(MinecraftClient client, int width, int height, int top, int bottom, Identifier backgroundLocation) { + super(client, width, height, top, bottom, backgroundLocation); + } + + @Override + public boolean changeFocus(boolean lookForwards) { + if (!this.inFocus && this.getItemCount() == 0) { + return false; + } else { + this.inFocus = !this.inFocus; + if (this.inFocus && this.getSelectedItem() == null && this.getItemCount() > 0) { + this.moveSelection(1); + } else if (this.inFocus && this.getSelectedItem() != null) { + this.getSelectedItem(); + } + + return this.inFocus; + } + } + + @Override + protected boolean isSelected(int index) { + return Objects.equals(this.getSelectedItem(), this.children().get(index)); + } + + @Override + protected int addItem(RuleEntry item) { + return super.addItem(item); + } + + @Override + public int getItemWidth() { + return width - 40; + } + + @Override + protected int getScrollbarPosition() { + return width - 14; + } + } + + public static abstract class RuleEntry extends DynamicElementListWidget.ElementEntry { + private final FilteringRule rule; + + public RuleEntry(FilteringRule rule) { + this.rule = rule; + } + + public FilteringRule getRule() { + return rule; + } + + @Override + public int getItemHeight() { + return 26; + } + + @Override + public boolean changeFocus(boolean lookForwards) { + return false; + } + } + + public static class DefaultRuleEntry extends RuleEntry { + private final ButtonWidget addButton; + private final BiFunction screenFunction; + + public DefaultRuleEntry(Screen parent, FilteringEntry entry, FilteringRule rule, BiFunction screenFunction) { + super(rule); + this.screenFunction = (screenFunction == null ? rule.createEntryScreen().orElse(null) : screenFunction); + addButton = new ButtonWidget(0, 0, 20, 20, Text.method_30163(" + "), button -> { + entry.edited = true; + MinecraftClient.getInstance().openScreen(this.screenFunction.apply(entry, parent)); + entry.rules.add(0, rule); + }); + addButton.active = this.screenFunction != null; + } + + @Override + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + MinecraftClient client = MinecraftClient.getInstance(); + { + Text title = getRule().getTitle(); + int i = client.textRenderer.getWidth(title); + if (i > entryWidth - 28) { + StringRenderable titleTrimmed = StringRenderable.concat(client.textRenderer.trimToWidth(title, entryWidth - 28 - client.textRenderer.getStringWidth("...")), StringRenderable.plain("...")); + client.textRenderer.drawWithShadow(matrices, titleTrimmed, x + 2, y + 1, 16777215); + } else { + client.textRenderer.drawWithShadow(matrices, title, x + 2, y + 1, 16777215); + } + } + { + Text subtitle = getRule().getSubtitle(); + int i = client.textRenderer.getWidth(subtitle); + if (i > entryWidth - 28) { + StringRenderable subtitleTrimmed = StringRenderable.concat(client.textRenderer.trimToWidth(subtitle, entryWidth - 28 - client.textRenderer.getStringWidth("...")), StringRenderable.plain("...")); + client.textRenderer.drawWithShadow(matrices, subtitleTrimmed, x + 2, y + 12, 8421504); + } else { + client.textRenderer.drawWithShadow(matrices, subtitle, x + 2, y + 12, 8421504); + } + } + addButton.x = x + entryWidth - 25; + addButton.y = y + 1; + addButton.render(matrices, mouseX, mouseY, delta); + } + + @Override + public List children() { + return Collections.singletonList(addButton); + } + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java index 4aecdef6f..1109e6b19 100644 --- a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java @@ -24,8 +24,10 @@ package me.shedaniel.rei.gui.config.entry; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; import me.shedaniel.rei.api.EntryStack; +import me.shedaniel.rei.impl.filtering.FilteringRule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.widget.AbstractButtonWidget; @@ -44,22 +46,27 @@ import java.util.function.Consumer; public class FilteringEntry extends AbstractConfigListEntry> { private int width; Consumer> saveConsumer; + Consumer>> rulesSaveConsumer; List defaultValue; List configFiltered; + List> rules; boolean edited = false; - private final FilteringScreen filteringScreen = new FilteringScreen(this); + final FilteringScreen filteringScreen = new FilteringScreen(this); + final FilteringRulesScreen filteringRulesScreen = new FilteringRulesScreen(this); private final AbstractButtonWidget buttonWidget = new ButtonWidget(0, 0, 0, 20, new TranslatableText("config.roughlyenoughitems.filteringScreen"), button -> { - filteringScreen.parent = MinecraftClient.getInstance().currentScreen; - MinecraftClient.getInstance().openScreen(filteringScreen); + filteringRulesScreen.parent = MinecraftClient.getInstance().currentScreen; + MinecraftClient.getInstance().openScreen(filteringRulesScreen); }); private final List children = ImmutableList.of(buttonWidget); - public FilteringEntry(int width, List configFiltered, List defaultValue, Consumer> saveConsumer) { + public FilteringEntry(int width, List configFiltered, List> rules, List defaultValue, Consumer> saveConsumer, Consumer>> rulesSaveConsumer) { super(NarratorManager.EMPTY, false); this.width = width; this.configFiltered = configFiltered; + this.rules = Lists.newArrayList(rules); this.defaultValue = defaultValue; this.saveConsumer = saveConsumer; + this.rulesSaveConsumer = rulesSaveConsumer; } @Override @@ -75,6 +82,7 @@ public class FilteringEntry extends AbstractConfigListEntry> { @Override public void save() { saveConsumer.accept(getValue()); + rulesSaveConsumer.accept(rules); this.edited = false; } diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRuleOptionsScreen.java b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRuleOptionsScreen.java new file mode 100644 index 000000000..21b0e0440 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRuleOptionsScreen.java @@ -0,0 +1,239 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.gui.config.entry; + +import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget; +import me.shedaniel.rei.impl.filtering.FilteringRule; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.StringRenderable; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Identifier; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class FilteringRuleOptionsScreen> extends Screen { + private final FilteringEntry entry; + private RulesList rulesList; + Screen parent; + public T rule; + + public FilteringRuleOptionsScreen(FilteringEntry entry, T rule, Screen screen) { + super(new TranslatableText("config.roughlyenoughitems.filteringRulesScreen")); + this.entry = entry; + this.rule = rule; + this.parent = screen; + } + + @Override + protected void init() { + super.init(); + if (rulesList != null) save(); + { + Text doneText = new TranslatableText("gui.done"); + int width = MinecraftClient.getInstance().textRenderer.getWidth(doneText); + addButton(new ButtonWidget(this.width - 4 - width - 10, 4, width + 10, 20, doneText, button -> { + save(); + client.openScreen(parent); + })); + } + rulesList = addChild(new RulesList(client, width, height, 30, height, BACKGROUND_TEXTURE)); + addEntries(ruleEntry -> rulesList.addItem(ruleEntry)); + } + + public abstract void addEntries(Consumer entryConsumer); + + public abstract void save(); + + public void addText(Consumer entryConsumer, StringRenderable text) { + for (StringRenderable s : textRenderer.wrapStringToWidthAsList(text, width - 80)) { + entryConsumer.accept(new TextRuleEntry(rule, s)); + } + } + + public void addEmpty(Consumer entryConsumer, int height) { + entryConsumer.accept(new EmptyRuleEntry(rule, height)); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.rulesList.render(matrices, mouseX, mouseY, delta); + super.render(matrices, mouseX, mouseY, delta); + this.textRenderer.drawWithShadow(matrices, this.title, this.width / 2.0F - this.textRenderer.getWidth(this.title) / 2.0F, 12.0F, -1); + } + + public static class RulesList extends DynamicElementListWidget { + public RulesList(MinecraftClient client, int width, int height, int top, int bottom, Identifier backgroundLocation) { + super(client, width, height, top, bottom, backgroundLocation); + } + + @Override + protected int addItem(RuleEntry item) { + return super.addItem(item); + } + + @Override + public int getItemWidth() { + return width - 40; + } + + @Override + protected int getScrollbarPosition() { + return width - 14; + } + } + + public static abstract class RuleEntry extends DynamicElementListWidget.ElementEntry { + private final FilteringRule rule; + + public RuleEntry(FilteringRule rule) { + this.rule = rule; + } + + public FilteringRule getRule() { + return rule; + } + } + + public static class TextRuleEntry extends RuleEntry { + private final StringRenderable text; + + public TextRuleEntry(FilteringRule rule, StringRenderable text) { + super(rule); + this.text = text; + } + + @Override + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, text, x + 5, y, -1); + } + + @Override + public int getItemHeight() { + return 12; + } + + @Override + public List children() { + return Collections.emptyList(); + } + } + + public static class EmptyRuleEntry extends RuleEntry { + private final int height; + + public EmptyRuleEntry(FilteringRule rule, int height) { + super(rule); + this.height = height; + } + + @Override + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + } + + @Override + public int getItemHeight() { + return height; + } + + @Override + public List children() { + return Collections.emptyList(); + } + } + + public static class TextFieldRuleEntry extends RuleEntry { + private final TextFieldWidget widget; + + public TextFieldRuleEntry(int width, FilteringRule rule, Consumer widgetConsumer) { + super(rule); + this.widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, width, 18, Text.method_30163("")); + widgetConsumer.accept(widget); + } + + @Override + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + widget.x = x + 2; + widget.y = y + 2; + widget.render(matrices, mouseX, mouseY, delta); + } + + @Override + public int getItemHeight() { + return 20; + } + + public TextFieldWidget getWidget() { + return widget; + } + + @Override + public List children() { + return Collections.singletonList(widget); + } + } + + public static class BooleanRuleEntry extends RuleEntry { + private boolean b; + private final ButtonWidget widget; + + public BooleanRuleEntry(int width, boolean b, FilteringRule rule, Function textFunction) { + super(rule); + this.b = b; + this.widget = new ButtonWidget(0, 0, 100, 20, textFunction.apply(b), button -> { + this.b = !this.b; + button.setMessage(textFunction.apply(this.b)); + }); + } + + public boolean getBoolean() { + return b; + } + + @Override + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + widget.x = x + 2; + widget.y = y; + widget.render(matrices, mouseX, mouseY, delta); + } + + @Override + public int getItemHeight() { + return 20; + } + + @Override + public List children() { + return Collections.singletonList(widget); + } + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRulesScreen.java b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRulesScreen.java new file mode 100644 index 000000000..8e5a3adae --- /dev/null +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRulesScreen.java @@ -0,0 +1,246 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.gui.config.entry; + +import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget; +import me.shedaniel.rei.impl.filtering.FilteringRule; +import me.shedaniel.rei.impl.filtering.rules.ManualFilteringRule; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.LiteralText; +import net.minecraft.text.StringRenderable; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Identifier; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; + +import static me.shedaniel.rei.gui.RecipeViewingScreen.CHEST_GUI_TEXTURE; + +public class FilteringRulesScreen extends Screen { + private final FilteringEntry entry; + private RulesList rulesList; + Screen parent; + + public FilteringRulesScreen(FilteringEntry entry) { + super(new TranslatableText("config.roughlyenoughitems.filteringRulesScreen")); + this.entry = entry; + } + + @Override + protected void init() { + super.init(); + { + Text backText = new LiteralText("↩ ").append(new TranslatableText("gui.back")); + addButton(new ButtonWidget(4, 4, MinecraftClient.getInstance().textRenderer.getWidth(backText) + 10, 20, backText, button -> { + client.openScreen(parent); + this.parent = null; + })); + } + { + Text addText = new LiteralText(" + "); + addButton(new ButtonWidget(width - 4 - 20, 4, 20, 20, addText, button -> { + FilteringAddRuleScreen screen = new FilteringAddRuleScreen(entry); + screen.parent = this; + client.openScreen(screen); + })); + } + rulesList = addChild(new RulesList(client, width, height, 30, height, BACKGROUND_TEXTURE)); + for (int i = entry.rules.size() - 1; i >= 0; i--) { + FilteringRule rule = entry.rules.get(i); + if (rule instanceof ManualFilteringRule) + rulesList.addItem(new DefaultRuleEntry(rule, entry, (entry, screen) -> { + entry.filteringScreen.parent = screen; + return entry.filteringScreen; + })); + else rulesList.addItem(new DefaultRuleEntry(rule, entry, null)); + } + rulesList.selectItem(rulesList.children().get(0)); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.rulesList.render(matrices, mouseX, mouseY, delta); + super.render(matrices, mouseX, mouseY, delta); + this.textRenderer.drawWithShadow(matrices, this.title, this.width / 2.0F - this.textRenderer.getWidth(this.title) / 2.0F, 12.0F, -1); + } + + public static class RulesList extends DynamicElementListWidget { + private boolean inFocus; + + public RulesList(MinecraftClient client, int width, int height, int top, int bottom, Identifier backgroundLocation) { + super(client, width, height, top, bottom, backgroundLocation); + } + + @Override + public boolean changeFocus(boolean lookForwards) { + if (!this.inFocus && this.getItemCount() == 0) { + return false; + } else { + this.inFocus = !this.inFocus; + if (this.inFocus && this.getSelectedItem() == null && this.getItemCount() > 0) { + this.moveSelection(1); + } else if (this.inFocus && this.getSelectedItem() != null) { + this.getSelectedItem(); + } + + return this.inFocus; + } + } + + @Override + protected boolean isSelected(int index) { + return Objects.equals(this.getSelectedItem(), this.children().get(index)); + } + + @Override + protected int addItem(RuleEntry item) { + return super.addItem(item); + } + + @Override + public boolean mouseClicked(double double_1, double double_2, int int_1) { + if (super.mouseClicked(double_1, double_2, int_1)) + return true; + RuleEntry item = getItemAtPosition(double_1, double_2); + if (item != null) { + client.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + selectItem(item); + this.setFocused(item); + this.setDragging(true); + return true; + } + return false; + } + + @Override + public int getItemWidth() { + return width - 40; + } + + @Override + protected int getScrollbarPosition() { + return width - 14; + } + } + + public static abstract class RuleEntry extends DynamicElementListWidget.ElementEntry { + private final FilteringRule rule; + + public RuleEntry(FilteringRule rule) { + this.rule = rule; + } + + public FilteringRule getRule() { + return rule; + } + + @Override + public int getItemHeight() { + return 26; + } + + @Override + public boolean changeFocus(boolean lookForwards) { + return false; + } + } + + public static class DefaultRuleEntry extends RuleEntry { + private final ButtonWidget configureButton; + private final ButtonWidget deleteButton; + private final BiFunction screenFunction; + + public DefaultRuleEntry(FilteringRule rule, FilteringEntry entry, BiFunction screenFunction) { + super(rule); + this.screenFunction = (screenFunction == null ? rule.createEntryScreen().orElse(null) : screenFunction); + configureButton = new ButtonWidget(0, 0, 20, 20, Text.method_30163(null), button -> { + entry.edited = true; + MinecraftClient.getInstance().openScreen(this.screenFunction.apply(entry, MinecraftClient.getInstance().currentScreen)); + }) { + @Override + protected void renderBg(MatrixStack matrices, MinecraftClient client, int mouseX, int mouseY) { + super.renderBg(matrices, client, mouseX, mouseY); + MinecraftClient.getInstance().getTextureManager().bindTexture(CHEST_GUI_TEXTURE); + drawTexture(matrices, x + 3, y + 3, 0, 0, 14, 14); + } + }; + { + Text deleteText = new TranslatableText("config.roughlyenoughitems.filteringRulesScreen.delete"); + deleteButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(deleteText) + 10, 20, deleteText, button -> { + final Screen screen = MinecraftClient.getInstance().currentScreen; + entry.edited = true; + entry.rules.remove(rule); + screen.init(MinecraftClient.getInstance(), screen.width, screen.height); + }); + } + configureButton.active = this.screenFunction != null; + deleteButton.active = !(rule instanceof ManualFilteringRule); + } + + @Override + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + MinecraftClient client = MinecraftClient.getInstance(); + { + Text title = getRule().getTitle(); + int i = client.textRenderer.getWidth(title); + if (i > entryWidth - 28) { + StringRenderable titleTrimmed = StringRenderable.concat(client.textRenderer.trimToWidth(title, entryWidth - 28 - client.textRenderer.getStringWidth("...")), StringRenderable.plain("...")); + client.textRenderer.drawWithShadow(matrices, titleTrimmed, x + 2, y + 1, 16777215); + } else { + client.textRenderer.drawWithShadow(matrices, title, x + 2, y + 1, 16777215); + } + } + { + Text subtitle = getRule().getSubtitle(); + int i = client.textRenderer.getWidth(subtitle); + if (i > entryWidth - 28) { + StringRenderable subtitleTrimmed = StringRenderable.concat(client.textRenderer.trimToWidth(subtitle, entryWidth - 28 - client.textRenderer.getStringWidth("...")), StringRenderable.plain("...")); + client.textRenderer.drawWithShadow(matrices, subtitleTrimmed, x + 2, y + 12, 8421504); + } else { + client.textRenderer.drawWithShadow(matrices, subtitle, x + 2, y + 12, 8421504); + } + } + configureButton.x = x + entryWidth - 25; + configureButton.y = y + 1; + configureButton.render(matrices, mouseX, mouseY, delta); + deleteButton.x = x + entryWidth - 27 - deleteButton.getWidth(); + deleteButton.y = y + 1; + deleteButton.render(matrices, mouseX, mouseY, delta); + } + + @Override + public List children() { + return Arrays.asList(configureButton, deleteButton); + } + } +} diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringScreen.java b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringScreen.java index 61b8c9d0a..2b9c84985 100644 --- a/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringScreen.java +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringScreen.java @@ -50,7 +50,6 @@ import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexFormats; -import net.minecraft.client.util.NarratorManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; @@ -103,6 +102,7 @@ public class FilteringScreen extends Screen { private ButtonWidget hideButton; private ButtonWidget showButton; private ButtonWidget backButton; + private Rectangle selectionCache; private List lastSearchArguments = Collections.emptyList(); @@ -134,6 +134,7 @@ public class FilteringScreen extends Screen { if (entry.isSelected() && !entry.isFiltered()) { filteringEntry.configFiltered.add(stack); filteringEntry.edited = true; + entry.dirty = true; } } }); @@ -147,6 +148,7 @@ public class FilteringScreen extends Screen { entry.getBounds().y = (int) (entry.backupY - scrolling.scrollAmount); if (entry.isSelected() && filteringEntry.configFiltered.remove(stack)) { filteringEntry.edited = true; + entry.dirty = true; } } }); @@ -207,6 +209,7 @@ public class FilteringScreen extends Screen { @Override public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { renderHoleBackground(matrices, 0, height, 32, 255, 255); + updateSelectionCache(); Rectangle bounds = getBounds(); tooltip = null; if (bounds.isEmpty()) @@ -269,6 +272,10 @@ public class FilteringScreen extends Screen { } private Rectangle getSelection() { + return selectionCache; + } + + private void updateSelectionCache() { if (selectionPoint != null) { Point p = secondPoint; if (p == null) { @@ -279,9 +286,10 @@ public class FilteringScreen extends Screen { int top = Math.min(p.y, selectionPoint.y); int right = Math.max(p.x, selectionPoint.x); int bottom = Math.max(p.y, selectionPoint.y); - return new Rectangle(left, (int) (top - scrolling.scrollAmount), right - left, bottom - top); + selectionCache = new Rectangle(left, (int) (top - scrolling.scrollAmount), right - left, bottom - top); + return; } - return new Rectangle(0, 0, 0, 0); + selectionCache = new Rectangle(0, 0, 0, 0); } @Override @@ -416,7 +424,7 @@ public class FilteringScreen extends Screen { if (!this.changeFocus(bl)) { this.changeFocus(bl); } - + return true; } return false; @@ -443,6 +451,8 @@ public class FilteringScreen extends Screen { private class EntryListEntry extends EntryWidget { private int backupY; + private boolean filtered = false; + private boolean dirty = true; private EntryListEntry(int x, int y) { super(new Point(x, y)); @@ -484,7 +494,11 @@ public class FilteringScreen extends Screen { } public boolean isFiltered() { - return CollectionUtils.findFirstOrNullEqualsEntryIgnoreAmount(filteringEntry.configFiltered, getCurrentEntry()) != null; + if (dirty) { + filtered = CollectionUtils.findFirstOrNullEqualsEntryIgnoreAmount(filteringEntry.configFiltered, getCurrentEntry()) != null; + dirty = false; + } + return filtered; } @Override diff --git a/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java b/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java index 4971c5c9b..6d83c91af 100644 --- a/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java +++ b/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java @@ -30,14 +30,12 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.widget.AbstractButtonWidget; import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.resource.language.I18n; import net.minecraft.client.util.NarratorManager; import net.minecraft.client.util.Window; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.TranslatableText; import org.jetbrains.annotations.ApiStatus; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -51,7 +49,7 @@ public class NoFilteringEntry extends AbstractConfigListEntry> private final AbstractButtonWidget buttonWidget = new ButtonWidget(0, 0, 0, 20, new TranslatableText("config.roughlyenoughitems.filteredEntries.loadWorldFirst"), button -> {}); private final List children = ImmutableList.of(buttonWidget); - public NoFilteringEntry(int width,List configFiltered, List defaultValue, Consumer> saveConsumer) { + public NoFilteringEntry(int width, List configFiltered, List defaultValue, Consumer> saveConsumer) { super(NarratorManager.EMPTY, false); this.width = width; this.configFiltered = configFiltered; diff --git a/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java b/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java index 6ffe07b06..79b4ccfd2 100644 --- a/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java +++ b/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java @@ -23,6 +23,7 @@ package me.shedaniel.rei.impl; +import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; @@ -49,6 +50,8 @@ import me.shedaniel.rei.gui.config.entry.NoFilteringEntry; import me.shedaniel.rei.gui.config.entry.RecipeScreenTypeEntry; import me.shedaniel.rei.gui.config.entry.ReloadPluginsEntry; import me.shedaniel.rei.gui.credits.CreditsScreen; +import me.shedaniel.rei.impl.filtering.FilteringRule; +import me.shedaniel.rei.impl.filtering.rules.ManualFilteringRule; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; @@ -58,6 +61,8 @@ import net.minecraft.client.gui.screen.ScreenTexts; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.StringNbtReader; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; @@ -97,6 +102,15 @@ public class ConfigManagerImpl implements ConfigManager { return new JsonPrimitive(gson.toJson(stack.toJson())); }).registerPrimitiveTypeAdapter(EntryStack.class, it -> { return it instanceof String ? EntryStack.readFromJson(gson.fromJson((String) it, JsonElement.class)) : null; + }).registerSerializer(FilteringRule.class, (rule, marshaller) -> { + return new JsonPrimitive(FilteringRule.toTag(rule, new CompoundTag()).toString()); + }).registerPrimitiveTypeAdapter(FilteringRule.class, it -> { + try { + return it instanceof String ? FilteringRule.fromTag(StringNbtReader.parse((String) it)) : null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } }).build())); GuiRegistry guiRegistry = AutoConfig.getGuiRegistry(ConfigObjectImpl.class); guiRegistry.registerPredicateProvider((i13n, field, config, defaults, guiProvider) -> { @@ -120,7 +134,7 @@ public class ConfigManagerImpl implements ConfigManager { REIHelper.getInstance().getPreviousContainerScreen() == null || MinecraftClient.getInstance().getNetworkHandler() == null || MinecraftClient.getInstance().getNetworkHandler().getRecipeManager() == null ? Collections.singletonList(new NoFilteringEntry(220, getUnsafely(field, config, new ArrayList<>()), getUnsafely(field, defaults), list -> setUnsafely(field, config, list))) : - Collections.singletonList(new FilteringEntry(220, getUnsafely(field, config, new ArrayList<>()), getUnsafely(field, defaults), list -> setUnsafely(field, config, list))) + Collections.singletonList(new FilteringEntry(220, getUnsafely(field, config, new ArrayList<>()), ((ConfigObjectImpl.Advanced.Filtering) config).filteringRules, getUnsafely(field, defaults), list -> setUnsafely(field, config, list), list -> ((ConfigObjectImpl.Advanced.Filtering) config).filteringRules = Lists.newArrayList(list))) , (field) -> field.getType() == List.class, ConfigObjectImpl.UseFilteringScreen.class); saveConfig(); RoughlyEnoughItemsCore.LOGGER.info("Config loaded."); @@ -136,6 +150,9 @@ public class ConfigManagerImpl implements ConfigManager { stack.setting(EntryStack.Settings.CHECK_AMOUNT, EntryStack.Settings.FALSE).setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.CHECK_TAGS, EntryStack.Settings.TRUE); } } + if (getConfig().getFilteringRules().stream().noneMatch(filteringRule -> filteringRule instanceof ManualFilteringRule)) { + getConfig().getFilteringRules().add(new ManualFilteringRule()); + } ((me.sargunvohra.mcmods.autoconfig1u.ConfigManager) AutoConfig.getConfigHolder(ConfigObjectImpl.class)).save(); } diff --git a/src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java b/src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java index df030349a..0945283dd 100644 --- a/src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java +++ b/src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java @@ -32,6 +32,7 @@ import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.rei.api.ConfigObject; import me.shedaniel.rei.api.EntryStack; import me.shedaniel.rei.gui.config.*; +import me.shedaniel.rei.impl.filtering.FilteringRule; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.util.InputUtil; @@ -56,7 +57,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @ConfigEntry.Category("functionality") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName private Functionality functionality = new Functionality(); @ConfigEntry.Category("advanced") @ConfigEntry.Gui.TransitiveObject @DontApplyFieldName - private Advanced advanced = new Advanced(); + public Advanced advanced = new Advanced(); @Override public boolean isOverlayVisible() { @@ -303,6 +304,11 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { return advanced.filtering.filteredStacks; } + @Override + public List> getFilteringRules() { + return advanced.filtering.filteringRules; + } + @Override @ApiStatus.Experimental public boolean shouldAsyncSearch() { @@ -414,7 +420,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @ConfigEntry.Gui.CollapsibleObject private Miscellaneous miscellaneous = new Miscellaneous(); @ConfigEntry.Gui.CollapsibleObject(startExpanded = true) - private Filtering filtering = new Filtering(); + public Filtering filtering = new Filtering(); public static class Tooltips { @Comment("Declares whether REI should append mod names to entries.") private boolean appendModNames = true; @@ -465,6 +471,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { public static class Filtering { @UseFilteringScreen private List filteredStacks = new ArrayList<>(); + @ConfigEntry.Gui.Excluded public List> filteringRules = new ArrayList<>(); } } } diff --git a/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java b/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java index 5dfa05331..b3274ae94 100644 --- a/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java +++ b/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java @@ -31,6 +31,10 @@ import me.shedaniel.rei.api.ConfigObject; import me.shedaniel.rei.api.EntryRegistry; import me.shedaniel.rei.api.EntryStack; import me.shedaniel.rei.api.RecipeHelper; +import me.shedaniel.rei.impl.filtering.FilteringContextImpl; +import me.shedaniel.rei.impl.filtering.FilteringRule; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.Pair; @@ -42,6 +46,7 @@ import java.util.*; import java.util.stream.Collectors; @ApiStatus.Internal +@Environment(EnvType.CLIENT) public class EntryRegistryImpl implements EntryRegistry { private final List preFilteredList = Lists.newCopyOnWriteArrayList(); @@ -117,14 +122,19 @@ public class EntryRegistryImpl implements EntryRegistry { public void refilter() { long started = System.currentTimeMillis(); - Collection filteredStacks = ConfigObject.getInstance().getFilteredStacks(); + FilteringContextImpl context = new FilteringContextImpl(getStacksList()); + List> rules = ConfigObject.getInstance().getFilteringRules(); + for (int i = rules.size() - 1; i >= 0; i--) { + context.handleResult(rules.get(i).processFilteredStacks(context)); + } preFilteredList.clear(); + Collection filteredStacks = context.getHiddenStacks(); for (EntryStack stack : getStacksList()) { if (findFirstOrNullEqualsEntryIgnoreAmount(filteredStacks, stack) == null) preFilteredList.add(stack); } long time = System.currentTimeMillis() - started; - RoughlyEnoughItemsCore.LOGGER.info("Refiltered %d entries in %dms.", filteredStacks.size(), time); + RoughlyEnoughItemsCore.LOGGER.info("Refiltered %d entries with %d rules in %dms.", filteredStacks.size(), rules.size(), time); } public void reset() { diff --git a/src/main/java/me/shedaniel/rei/impl/SearchArgument.java b/src/main/java/me/shedaniel/rei/impl/SearchArgument.java index 52696a2b8..ffeb1f439 100644 --- a/src/main/java/me/shedaniel/rei/impl/SearchArgument.java +++ b/src/main/java/me/shedaniel/rei/impl/SearchArgument.java @@ -33,6 +33,8 @@ import me.shedaniel.rei.impl.search.Argument; import me.shedaniel.rei.impl.search.ArgumentsRegistry; import me.shedaniel.rei.impl.search.MatchStatus; import me.shedaniel.rei.utils.CollectionUtils; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import org.apache.commons.lang3.StringUtils; @@ -44,6 +46,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; @ApiStatus.Internal +@Environment(EnvType.CLIENT) public class SearchArgument { public static final String SPACE = " ", EMPTY = ""; private static final SearchArgument ALWAYS = new SearchArgument(AlwaysMatchingArgument.INSTANCE, EMPTY, true); @@ -72,7 +75,7 @@ public class SearchArgument { List arguments = Lists.newArrayList(); while (terms.find()) { String term = MoreObjects.firstNonNull(terms.group(1), terms.group(2)); - for (Argument argument : ArgumentsRegistry.ARGUMENTS) { + for (Argument argument : ArgumentsRegistry.ARGUMENT_LIST) { MatchStatus status = argument.matchesArgumentPrefix(term); if (status.isMatched()) { arguments.add(new SearchArgument(argument, status.getText(), !status.isInverted(), !status.shouldPreserveCasing())); @@ -99,7 +102,7 @@ public class SearchArgument { if (searchArguments.isEmpty()) return true; MinecraftClient minecraft = MinecraftClient.getInstance(); - Object[] data = new Object[ArgumentsRegistry.ARGUMENTS.size()]; + Object[] data = new Object[ArgumentsRegistry.ARGUMENT_LIST.size()]; for (SearchArgument.SearchArguments arguments : searchArguments) { boolean applicable = true; for (SearchArgument argument : arguments.getArguments()) { @@ -149,6 +152,10 @@ public class SearchArgument { public SearchArgument[] getArguments() { return arguments; } + + public final boolean isAlways() { + return this == ALWAYS; + } } } diff --git a/src/main/java/me/shedaniel/rei/impl/filtering/AbstractFilteringRule.java b/src/main/java/me/shedaniel/rei/impl/filtering/AbstractFilteringRule.java new file mode 100644 index 000000000..0cd4b523d --- /dev/null +++ b/src/main/java/me/shedaniel/rei/impl/filtering/AbstractFilteringRule.java @@ -0,0 +1,40 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.filtering; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public abstract class AbstractFilteringRule> implements FilteringRule { + @Override + public boolean equals(Object obj) { + return getClass() == obj.getClass(); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } +} diff --git a/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContext.java b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContext.java new file mode 100644 index 000000000..f6066597b --- /dev/null +++ b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContext.java @@ -0,0 +1,52 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.filtering; + +import me.shedaniel.rei.api.EntryStack; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Map; +import java.util.Set; + +@ApiStatus.Internal +@ApiStatus.Experimental +public interface FilteringContext { + Map> getStacks(); + + default Set getStacks(FilteringContextType type) { + return getStacks().get(type); + } + + default Set getShownStacks() { + return getStacks(FilteringContextType.SHOWN); + } + + default Set getUnsetStacks() { + return getStacks(FilteringContextType.DEFAULT); + } + + default Set getHiddenStacks() { + return getStacks(FilteringContextType.HIDDEN); + } +} diff --git a/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextImpl.java b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextImpl.java new file mode 100644 index 000000000..830593d62 --- /dev/null +++ b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextImpl.java @@ -0,0 +1,63 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.filtering; + +import com.google.common.collect.Maps; +import me.shedaniel.rei.api.EntryStack; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import java.util.*; + +@Environment(EnvType.CLIENT) +public class FilteringContextImpl implements FilteringContext { + private final Map> stacks; + + public FilteringContextImpl(List allStacks) { + this(Maps.newHashMap()); + getUnsetStacks().addAll(allStacks); + } + + public FilteringContextImpl(Map> stacks) { + this.stacks = stacks; + for (FilteringContextType type : FilteringContextType.values()) { + this.stacks.computeIfAbsent(type, t -> new TreeSet<>(Comparator.comparing(EntryStack::hashIgnoreAmount))); + } + } + + @Override + public Map> getStacks() { + return stacks; + } + + public void handleResult(FilteringResult result) { + getUnsetStacks().removeAll(result.getHiddenStacks()); + getShownStacks().removeAll(result.getHiddenStacks()); + getHiddenStacks().addAll(result.getHiddenStacks()); + + getHiddenStacks().removeAll(result.getShownStacks()); + getUnsetStacks().removeAll(result.getShownStacks()); + getShownStacks().addAll(result.getShownStacks()); + } +} diff --git a/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextType.java b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextType.java new file mode 100644 index 000000000..72954da6e --- /dev/null +++ b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextType.java @@ -0,0 +1,34 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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.filtering; + +public enum FilteringContextType { + SHOWN, + DEFAULT, + HIDDEN; + + public boolean isHidden() { + return this == HIDDEN; + } +} diff --git a/src/main/java/me/shedaniel/rei/impl/filtering/FilteringResult.java b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringResult.java new file mode 100644 index 000000000..e0d0df1bc --- /dev/null +++ b/src/main/java/me/shedaniel/rei/impl/filtering/FilteringResult.java @@ -0,0 +1,67 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 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,