diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index 953316691..57e1d5bd8 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -13,6 +13,7 @@ import com.terraformersmc.modmenu.util.TranslationUtil; import com.terraformersmc.modmenu.util.mod.Mod; import com.terraformersmc.modmenu.util.mod.ModBadgeRenderer; +import com.terraformersmc.modmenu.util.mod.search.ModSearch; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.gui.screen.ConfirmLinkScreen; @@ -52,6 +53,7 @@ public class ModsScreen extends Screen { private static final Text CONFIGURE = Text.translatable("modmenu.configure"); private static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu | ModsScreen"); private TextFieldWidget searchBox; + private ModSearch search; private DescriptionListWidget descriptionListWidget; private final Screen previousScreen; private ModListWidget modList; @@ -104,7 +106,6 @@ protected void init() { int searchBoxWidth = ModMenuConfig.CONFIG_MODE.getValue() ? Math.min(200, searchWidthMax) : searchWidthMax; searchBoxX = paneWidth / 2 - searchBoxWidth / 2 - filtersButtonSize / 2; this.searchBox = new TextFieldWidget(this.textRenderer, searchBoxX, 22, searchBoxWidth, 20, this.searchBox, Text.translatable("modmenu.search")); - this.searchBox.setChangedListener((string_1) -> this.modList.filter(string_1, false)); for (Mod mod : ModMenu.MODS.values()) { if (!modHasConfigScreen.containsKey(mod.getId())) { @@ -122,8 +123,9 @@ protected void init() { } this.modList = new ModListWidget(this.client, paneWidth, this.height, paneY, this.height - 36, ModMenuConfig.COMPACT_LIST.getValue() ? 23 : 36, this.searchBox.getText(), this.modList, this); + this.search = new ModSearch(this, this.modList, this.searchBox); this.modList.setLeftPos(0); - modList.reloadFilters(); + modList.refreshEntries(); this.descriptionListWidget = new DescriptionListWidget(this.client, paneWidth, this.height, RIGHT_PANE_Y + 60, this.height - 36, textRenderer.fontHeight + 1, this); this.descriptionListWidget.setLeftPos(rightPaneX); @@ -208,7 +210,7 @@ public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { this.addDrawableChild(new ButtonWidget(filtersX, 45, sortingWidth, 20, sortingText, button -> { ModMenuConfig.SORTING.cycleValue(); ModMenuConfigManager.save(); - modList.reloadFilters(); + modList.refreshEntries(); }, Supplier::get) { @Override public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { @@ -221,7 +223,7 @@ public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { this.addDrawableChild(new ButtonWidget(filtersX + sortingWidth + 2, 45, showLibrariesWidth, 20, showLibrariesText, button -> { ModMenuConfig.SHOW_LIBRARIES.toggleValue(); ModMenuConfigManager.save(); - modList.reloadFilters(); + modList.refreshEntries(); }, Supplier::get) { @Override public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { @@ -421,8 +423,8 @@ public void updateScrollPercent(double scrollPercent) { this.scrollPercent = scrollPercent; } - public String getSearchInput() { - return searchBox.getText(); + public ModSearch getSearch() { + return search; } private boolean updateFiltersX() { diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java index cf3be30d6..75a27b5b7 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java @@ -10,7 +10,6 @@ import com.terraformersmc.modmenu.gui.widget.entries.ParentEntry; import com.terraformersmc.modmenu.util.mod.Mod; import com.terraformersmc.modmenu.util.mod.fabric.FabricIconHandler; -import com.terraformersmc.modmenu.util.mod.ModSearch; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.client.render.BufferBuilder; @@ -42,7 +41,6 @@ public ModListWidget(MinecraftClient client, int width, int height, int y1, int if (list != null) { this.mods = list.mods; } - this.filter(searchTerm, false); setScrollAmount(parent.getScrollPercent() * Math.max(0, this.getMaxPosition() - (this.bottom - this.top - 4))); } @@ -108,16 +106,7 @@ protected ModListEntry remove(int index) { return super.remove(index); } - public void reloadFilters() { - filter(parent.getSearchInput(), true, false); - } - - - public void filter(String searchTerm, boolean refresh) { - filter(searchTerm, refresh, true); - } - - private void filter(String searchTerm, boolean refresh, boolean search) { + public void refreshEntries() { this.clearEntries(); addedMods.clear(); Collection mods = ModMenu.MODS.values().stream().filter(mod -> { @@ -136,13 +125,13 @@ private void filter(String searchTerm, boolean refresh, boolean search) { // mods.addAll(TestModContainer.getTestModContainers()); } - if (this.mods == null || refresh) { + if (this.mods == null) { this.mods = new ArrayList<>(); this.mods.addAll(mods); this.mods.sort(ModMenuConfig.SORTING.getValue().getComparator()); } - List matched = ModSearch.search(parent, searchTerm, this.mods); + List matched = this.parent.getSearch().getResults(mods); for (Mod mod : matched) { String modId = mod.getId(); @@ -157,11 +146,14 @@ private void filter(String searchTerm, boolean refresh, boolean search) { //Add parent mods when not searching List children = ModMenu.PARENT_MAP.get(mod); children.sort(ModMenuConfig.SORTING.getValue().getComparator()); - ParentEntry parent = new ParentEntry(mod, children, this); + + //Get valid children + List validChildren = children.stream().filter(matched::contains).collect(Collectors.toList()); + + ParentEntry parent = new ParentEntry(mod, children, validChildren.size(), this); this.addEntry(parent); //Add children if they are meant to be shown if (this.parent.showModChildren.contains(modId)) { - List validChildren = ModSearch.search(this.parent, searchTerm, children); for (Mod child : validChildren) { this.addEntry(new ChildEntry(child, parent, this, validChildren.indexOf(child) == validChildren.size() - 1)); } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java index 11d7e7e11..42d529dad 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java @@ -5,7 +5,6 @@ import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.gui.widget.ModListWidget; import com.terraformersmc.modmenu.util.mod.Mod; -import com.terraformersmc.modmenu.util.mod.ModSearch; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.util.math.MatrixStack; @@ -20,12 +19,14 @@ public class ParentEntry extends ModListEntry { private static final Identifier PARENT_MOD_TEXTURE = new Identifier(ModMenu.MOD_ID, "textures/gui/parent_mod.png"); protected List children; + protected int shownChildren; protected ModListWidget list; protected boolean hoveringIcon = false; - public ParentEntry(Mod parent, List children, ModListWidget list) { + public ParentEntry(Mod parent, List children, int shownChildren, ModListWidget list) { super(parent, list); this.children = children; + this.shownChildren = shownChildren; this.list = list; } @@ -35,7 +36,6 @@ public void render(MatrixStack matrices, int index, int y, int x, int rowWidth, TextRenderer font = client.textRenderer; int childrenBadgeHeight = font.fontHeight; int childrenBadgeWidth = font.fontHeight; - int shownChildren = ModSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); Text str = shownChildren == children.size() ? Text.literal(String.valueOf(shownChildren)) : Text.literal(shownChildren + "/" + children.size()); int childrenWidth = font.getWidth(str) - 1; if (childrenBadgeWidth < childrenWidth + 4) { @@ -72,7 +72,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int i) { } else { list.getParent().showModChildren.add(id); } - list.filter(list.getParent().getSearchInput(), false); + list.refreshEntries(); } return super.mouseClicked(mouseX, mouseY, i); } @@ -86,18 +86,18 @@ public boolean keyPressed(int keyCode, int scanCode, int modifiers) { } else { list.getParent().showModChildren.add(modId); } - list.filter(list.getParent().getSearchInput(), false); + list.refreshEntries(); return true; } else if (keyCode == GLFW.GLFW_KEY_LEFT) { if (list.getParent().showModChildren.contains(modId)) { list.getParent().showModChildren.remove(modId); - list.filter(list.getParent().getSearchInput(), false); + list.refreshEntries(); } return true; } else if (keyCode == GLFW.GLFW_KEY_RIGHT) { if (!list.getParent().showModChildren.contains(modId)) { list.getParent().showModChildren.add(modId); - list.filter(list.getParent().getSearchInput(), false); + list.refreshEntries(); } else { return list.keyPressed(GLFW.GLFW_KEY_DOWN, 0, 0); } diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java b/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java index c2e3df964..845cee959 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java @@ -8,6 +8,7 @@ import net.minecraft.client.resource.language.I18n; import net.minecraft.client.texture.NativeImageBackedTexture; import net.minecraft.text.Text; +import net.minecraft.text.TextColor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.quiltmc.loader.api.QuiltLoader; @@ -115,22 +116,26 @@ default String getTranslatedDescription() { boolean getChildHasUpdate(); enum Badge { - LIBRARY("modmenu.badge.library", 0xff107454, 0xff093929, "library"), - CLIENT("modmenu.badge.clientsideOnly", 0xff2b4b7c, 0xff0e2a55, null), - DEPRECATED("modmenu.badge.deprecated", 0xff841426, 0xff530C17, "deprecated"), - PATCHWORK_FORGE("modmenu.badge.forge", 0xff1f2d42, 0xff101721, null), - MODPACK("modmenu.badge.modpack", 0xff7a2b7c, 0xff510d54, null), - MINECRAFT("modmenu.badge.minecraft", 0xff6f6c6a, 0xff31302f, null); + LIBRARY("modmenu.badge.library", "modmenu.searchTerms.library", 0xff107454, 0xff093929, 0xff4ce6b5, "library"), + CLIENT("modmenu.badge.clientsideOnly", "modmenu.searchTerms.clientside", 0xff2b4b7c, 0xff0e2a55, 0xff3484fe, null), + DEPRECATED("modmenu.badge.deprecated", "modmenu.searchTerms.deprecated", 0xff841426, 0xff530C17, 0xffe44e66, "deprecated"), + PATCHWORK_FORGE("modmenu.badge.forge", "modmenu.searchTerms.patchwork", 0xff1f2d42, 0xff101721, 0xff7a93b8, null), + MODPACK("modmenu.badge.modpack", "modmenu.searchTerms.modpack", 0xff7a2b7c, 0xff510d54, 0xffc868ca, null), + MINECRAFT("modmenu.badge.minecraft", null, 0xff6f6c6a, 0xff31302f, 0xff9b9997, null); private final Text text; private final int outlineColor, fillColor; + private final TextColor searchColor; private final String key; + private final String searchKey; private static final Map KEY_MAP = new HashMap<>(); - Badge(String translationKey, int outlineColor, int fillColor, String key) { + Badge(String translationKey, String searchKey, int outlineColor, int fillColor, int searchColor, String key) { this.text = Text.translatable(translationKey); + this.searchKey = searchKey; this.outlineColor = outlineColor; this.fillColor = fillColor; + this.searchColor = TextColor.fromRgb(searchColor); this.key = key; } @@ -146,6 +151,14 @@ public int getFillColor() { return this.fillColor; } + public TextColor getSearchColor() { + return this.searchColor; + } + + public String getSearchKey() { + return this.searchKey; + } + public static Set convert(Set badgeKeys) { return badgeKeys.stream().map(KEY_MAP::get).collect(Collectors.toSet()); } diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/ModSearch.java b/src/main/java/com/terraformersmc/modmenu/util/mod/ModSearch.java deleted file mode 100644 index 27b7e8b14..000000000 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/ModSearch.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.terraformersmc.modmenu.util.mod; - -import com.terraformersmc.modmenu.ModMenu; -import com.terraformersmc.modmenu.gui.ModsScreen; -import net.minecraft.client.resource.language.I18n; - -import java.util.List; -import java.util.Locale; -import java.util.stream.Collectors; - -public class ModSearch { - - public static boolean validSearchQuery(String query) { - return query != null && !query.isEmpty(); - } - - public static List search(ModsScreen screen, String query, List candidates) { - if (!validSearchQuery(query)) { - return candidates; - } - return candidates.stream() - .filter(modContainer -> passesFilters(screen, modContainer, query.toLowerCase(Locale.ROOT))) - .collect(Collectors.toList()); - } - - private static boolean passesFilters(ModsScreen screen, Mod mod, String query) { - String modId = mod.getId(); - String modName = mod.getName(); - String modTranslatedName = mod.getTranslatedName(); - String modDescription = mod.getDescription(); - String modTranslatedDescription = mod.getTranslatedDescription(); - String modSummary = mod.getSummary(); - - String library = I18n.translate("modmenu.searchTerms.library"); - String patchwork = I18n.translate("modmenu.searchTerms.patchwork"); - String modpack = I18n.translate("modmenu.searchTerms.modpack"); - String deprecated = I18n.translate("modmenu.searchTerms.deprecated"); - String clientside = I18n.translate("modmenu.searchTerms.clientside"); - String configurable = I18n.translate("modmenu.searchTerms.configurable"); - String hasUpdate = I18n.translate("modmenu.searchTerms.hasUpdate"); - - // Some basic search, could do with something more advanced but this will do for now - if (modName.toLowerCase(Locale.ROOT).contains(query) // Search default mod name - || modTranslatedName.toLowerCase(Locale.ROOT).contains(query) // Search localized mod name - || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod ID - || modDescription.toLowerCase(Locale.ROOT).contains(query) // Search default mod description - || modTranslatedDescription.toLowerCase(Locale.ROOT).contains(query) // Search localized mod description - || modSummary.toLowerCase(Locale.ROOT).contains(query) // Search mod summary - || authorMatches(mod, query) // Search via author - || library.contains(query) && mod.getBadges().contains(Mod.Badge.LIBRARY) // Search for lib mods - || patchwork.contains(query) && mod.getBadges().contains(Mod.Badge.PATCHWORK_FORGE) // Search for patchwork mods - || modpack.contains(query) && mod.getBadges().contains(Mod.Badge.MODPACK) // Search for modpack mods - || deprecated.contains(query) && mod.getBadges().contains(Mod.Badge.DEPRECATED) // Search for deprecated mods - || clientside.contains(query) && mod.getBadges().contains(Mod.Badge.CLIENT) // Search for clientside mods - || configurable.contains(query) && screen.getModHasConfigScreen().get(modId) // Search for mods that can be configured - || hasUpdate.contains(query) && mod.getModrinthData() != null // Search for mods that have updates - ) { - return true; - } - - // Allow parent to pass filter if a child passes - if (ModMenu.PARENT_MAP.keySet().contains(mod)) { - for (Mod child : ModMenu.PARENT_MAP.get(mod)) { - if (passesFilters(screen, child, query)) { - return true; - } - } - } - return false; - } - - private static boolean authorMatches(Mod mod, String query) { - return mod.getAuthors().stream() - .map(s -> s.toLowerCase(Locale.ROOT)) - .anyMatch(s -> s.contains(query.toLowerCase(Locale.ROOT))); - } - -} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/ModSearch.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/ModSearch.java new file mode 100644 index 000000000..fa9b9b729 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/ModSearch.java @@ -0,0 +1,60 @@ +package com.terraformersmc.modmenu.util.mod.search; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import com.terraformersmc.modmenu.ModMenu; +import com.terraformersmc.modmenu.gui.ModsScreen; +import com.terraformersmc.modmenu.gui.widget.ModListWidget; +import com.terraformersmc.modmenu.util.mod.Mod; + +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.text.OrderedText; + +public class ModSearch { + private final ModsScreen screen; + private final ModListWidget modList; + + private SearchQuery query; + + public ModSearch(ModsScreen screen, ModListWidget modList, TextFieldWidget searchBox) { + this.screen = screen; + this.modList = modList; + + this.query = SearchQuery.parse("", this.screen); + + searchBox.setChangedListener(this::updateSearch); + searchBox.setRenderTextProvider(this::provideRenderText); + } + + private void updateSearch(String string) { + this.query = SearchQuery.parse(string, this.screen); + this.modList.refreshEntries(); + } + + private OrderedText provideRenderText(String original, int firstCharacterIndex) { + return this.query.provideRenderText(firstCharacterIndex, original.length()); + } + + private boolean matches(Mod mod) { + if (this.query.matches(mod)) { + return true; + } + + // Allow parent to pass filter if a child passes + if (ModMenu.PARENT_MAP.keySet().contains(mod)) { + for (Mod child : ModMenu.PARENT_MAP.get(mod)) { + if (this.query.matches(child)) { + return true; + } + } + } + + return false; + } + + public List getResults(Collection mods) { + return mods.stream().filter(this::matches).collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/SearchQuery.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/SearchQuery.java new file mode 100644 index 000000000..870dd2156 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/SearchQuery.java @@ -0,0 +1,131 @@ +package com.terraformersmc.modmenu.util.mod.search; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.terraformersmc.modmenu.gui.ModsScreen; +import com.terraformersmc.modmenu.util.mod.Mod; +import com.terraformersmc.modmenu.util.mod.search.term.BadgeSearchTerm; +import com.terraformersmc.modmenu.util.mod.search.term.ConfigurableSearchTerm; +import com.terraformersmc.modmenu.util.mod.search.term.ContentSearchTerm; +import com.terraformersmc.modmenu.util.mod.search.term.HasUpdateSearchTerm; +import com.terraformersmc.modmenu.util.mod.search.term.SearchTerm; +import com.terraformersmc.modmenu.util.mod.search.term.TermData; + +import net.minecraft.client.resource.language.I18n; +import net.minecraft.text.OrderedText; +import net.minecraft.text.Style; + +public class SearchQuery { + private static final Pattern TERM_SEPARATOR = Pattern.compile("(\\s+|$)"); + + private final List terms; + + protected SearchQuery(List terms) { + this.terms = terms; + } + + protected OrderedText provideRenderText(int start, int length) { + List texts = new ArrayList<>(); + int end = start + length; + + for (SearchTerm term : this.terms) { + TermData data = term.getData(); + String string = data.getContentWithWhitespace(); + + int termStart = data.getStart(); + int termEnd = termStart + string.length(); + + if (termEnd < start || termStart > end) { + continue; + } else if (termEnd > end) { + string = string.substring(0, end - termStart); + } else if (start > termStart) { + string = string.substring(start - termStart); + } + + Style style = term.getStyle(); + texts.add(OrderedText.styledForwardsVisitedString(string, style)); + + start += string.length(); + } + + return OrderedText.concat(texts); + } + + protected boolean matches(Mod mod) { + for (SearchTerm term : this.terms) { + if (!term.matches(mod)) { + return false; + } + } + + return true; + } + + @Override + public String toString() { + return "SearchQuery{terms=" + this.terms + "}"; + } + + protected static SearchQuery parse(String string, ModsScreen screen) { + List terms = new ArrayList<>(); + + Matcher matcher = TERM_SEPARATOR.matcher(string); + int start = 0; + + while (matcher.find()) { + TermData data = TermData.of(matcher, string, start); + start = matcher.end(); + + SearchTerm term = SearchQuery.parseTerm(data, screen); + terms.add(term); + } + + return new SearchQuery(terms); + } + + protected static SearchTerm parseTerm(TermData data, ModsScreen screen) { + String content = data.getContent(); + + if (content.startsWith("@")) { + String keyword = content.substring(1); + + BadgeSearchTerm term = getBadgeFromKeyword(keyword, data); + if (term != null) { + return term; + } else if (SearchQuery.isKeyword(keyword, "modmenu.searchTerms.configurable")) { + return new ConfigurableSearchTerm(data, screen); + } else if (SearchQuery.isKeyword(keyword, "modmenu.searchTerms.hasUpdate")) { + return new HasUpdateSearchTerm(data); + } + } + + return new ContentSearchTerm(data); + } + + protected static BadgeSearchTerm getBadgeFromKeyword(String keyword, TermData data) { + for (Mod.Badge badge : Mod.Badge.values()) { + if (isKeyword(keyword, badge.getSearchKey())) { + return new BadgeSearchTerm(data, badge); + } + } + return null; + } + + protected static boolean isKeyword(String keyword, String translationKey) { + if (translationKey == null) return false; + + String translated = I18n.translate(translationKey); + + for (String option : translated.split(" ")) { + if (keyword.equals(option)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/BadgeSearchTerm.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/BadgeSearchTerm.java new file mode 100644 index 000000000..44c433483 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/BadgeSearchTerm.java @@ -0,0 +1,24 @@ +package com.terraformersmc.modmenu.util.mod.search.term; + +import com.terraformersmc.modmenu.util.mod.Mod; + +import net.minecraft.text.TextColor; + +public class BadgeSearchTerm extends SearchTerm { + private final Mod.Badge badge; + + public BadgeSearchTerm(TermData data, Mod.Badge badge) { + super(data); + this.badge = badge; + } + + @Override + public boolean matches(Mod mod) { + return mod.getBadges().contains(this.badge); + } + + @Override + public TextColor getColor() { + return this.badge.getSearchColor(); + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/ConfigurableSearchTerm.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/ConfigurableSearchTerm.java new file mode 100644 index 000000000..252e94633 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/ConfigurableSearchTerm.java @@ -0,0 +1,27 @@ +package com.terraformersmc.modmenu.util.mod.search.term; + +import com.terraformersmc.modmenu.gui.ModsScreen; +import com.terraformersmc.modmenu.util.mod.Mod; + +import net.minecraft.text.TextColor; + +public class ConfigurableSearchTerm extends SearchTerm { + private static final TextColor COLOR = TextColor.fromRgb(0x65bbd8); + + private final ModsScreen screen; + + public ConfigurableSearchTerm(TermData data, ModsScreen screen) { + super(data); + this.screen = screen; + } + + @Override + public boolean matches(Mod mod) { + return screen.getModHasConfigScreen().get(mod.getId()); + } + + @Override + public TextColor getColor() { + return COLOR; + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/ContentSearchTerm.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/ContentSearchTerm.java new file mode 100644 index 000000000..859d43518 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/ContentSearchTerm.java @@ -0,0 +1,45 @@ +package com.terraformersmc.modmenu.util.mod.search.term; + +import java.util.Locale; + +import com.terraformersmc.modmenu.util.mod.Mod; + +import net.minecraft.text.TextColor; + +public class ContentSearchTerm extends SearchTerm { + private final String content; + + public ContentSearchTerm(TermData data) { + super(data); + this.content = data.getContent().toLowerCase(Locale.ROOT); + } + + @Override + public boolean matches(Mod mod) { + String modName = mod.getName().toLowerCase(Locale.ROOT); + String modTranslatedName = mod.getTranslatedName().toLowerCase(Locale.ROOT); + String modId = mod.getId().toLowerCase(Locale.ROOT); + String modDescription = mod.getDescription().toLowerCase(Locale.ROOT); + String modTranslatedDescription = mod.getDescription().toLowerCase(Locale.ROOT); + String modSummary = mod.getSummary().toLowerCase(Locale.ROOT); + + return modName.contains(this.content) // Search mod name + || modTranslatedName.contains(this.content) // Search mod translated name + || modId.contains(this.content) // Search mod ID + || modDescription.contains(this.content) // Search mod description + || modTranslatedDescription.contains(this.content) // Search mod translated description + || modSummary.contains(this.content) // Search mod summary + || ContentSearchTerm.authorMatches(mod, this.content); // Search via author + } + + @Override + public TextColor getColor() { + return null; + } + + private static boolean authorMatches(Mod mod, String query) { + return mod.getAuthors().stream() + .map(s -> s.toLowerCase(Locale.ROOT)) + .anyMatch(s -> s.contains(query)); + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/HasUpdateSearchTerm.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/HasUpdateSearchTerm.java new file mode 100644 index 000000000..06e6e18dd --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/HasUpdateSearchTerm.java @@ -0,0 +1,23 @@ +package com.terraformersmc.modmenu.util.mod.search.term; + +import com.terraformersmc.modmenu.util.mod.Mod; + +import net.minecraft.text.TextColor; + +public class HasUpdateSearchTerm extends SearchTerm { + private static final TextColor COLOR = TextColor.fromRgb(0xaad865); + + public HasUpdateSearchTerm(TermData data) { + super(data); + } + + @Override + public boolean matches(Mod mod) { + return mod.getModrinthData() != null; + } + + @Override + public TextColor getColor() { + return COLOR; + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/SearchTerm.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/SearchTerm.java new file mode 100644 index 000000000..6464f612b --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/SearchTerm.java @@ -0,0 +1,29 @@ +package com.terraformersmc.modmenu.util.mod.search.term; + +import org.jetbrains.annotations.Nullable; + +import com.terraformersmc.modmenu.util.mod.Mod; + +import net.minecraft.text.Style; +import net.minecraft.text.TextColor; + +public abstract class SearchTerm { + private final TermData data; + + public SearchTerm(TermData data) { + this.data = data; + } + + public TermData getData() { + return this.data; + } + + public abstract boolean matches(Mod mod); + + @Nullable + public abstract TextColor getColor(); + + public final Style getStyle() { + return Style.EMPTY.withColor(this.getColor()); + } +} diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/TermData.java b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/TermData.java new file mode 100644 index 000000000..9217201e5 --- /dev/null +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/search/term/TermData.java @@ -0,0 +1,39 @@ +package com.terraformersmc.modmenu.util.mod.search.term; + +import java.util.regex.Matcher; + +public class TermData { + private final String content; + private final String contentWithWhitespace; + private final int start; + + public TermData(String content, String contentWithWhitespace, int start) { + this.content = content; + this.contentWithWhitespace = contentWithWhitespace; + this.start = start; + } + + public String getContent() { + return this.content; + } + + public String getContentWithWhitespace() { + return this.contentWithWhitespace; + } + + public int getStart() { + return this.start; + } + + @Override + public String toString() { + return "TermData{'" + this.contentWithWhitespace + "', " + this.start + "}"; + } + + public static TermData of(Matcher matcher, String string, int start) { + String content = string.substring(start, matcher.start()); + String contentWithWhitespace = string.substring(start, matcher.end()); + + return new TermData(content, contentWithWhitespace, start); + } +}