From c1b089783e8090800efef1882915496278256eeb Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 26 Sep 2026 21:42:55 +0300 Subject: [PATCH 01/59] Make hardcoded text translatable --- .../java/io/github/prospector/modmenu/gui/ModListScreen.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index c5ce372de..6f3908394 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -186,8 +186,8 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { this.buttons.add(configureButton); this.buttons.add(websiteButton); this.buttons.add(issuesButton); - this.buttons.add(ButtonUtil.createButton(MODS_FOLDER_BUTTON_ID, this.width / 2 - 154, this.height - 28, 150, 20, "Open Mods Folder")); - this.buttons.add(ButtonUtil.createButton(DONE_BUTTON_ID, this.width / 2 + 4, this.height - 28, 150, 20, "Done")); + this.buttons.add(ButtonUtil.createButton(MODS_FOLDER_BUTTON_ID, this.width / 2 - 154, this.height - 28, 150, 20, I18n.getInstance().translateKey("modmenu.modsFolder"))); + this.buttons.add(ButtonUtil.createButton(DONE_BUTTON_ID, this.width / 2 + 4, this.height - 28, 150, 20, I18n.getInstance().translateKey("modmenu.done"))); this.searchBox.setFocused(true); init = true; From 47dee80b7a508fc8e12a182aa5f937d3183f30f4 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 00:57:16 +0300 Subject: [PATCH 02/59] Update ModListScreen.java --- .../prospector/modmenu/gui/ModListScreen.java | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 6f3908394..07366dc38 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -253,9 +253,9 @@ public ModListWidget getModList() { @Override public void keyPressed(char char_1, int int_1, int mouseX, int mouseY) { this.searchBox.textboxKeyTyped(char_1, int_1); - if (int_1 == 1) { - this.mc.displayScreen(this.parent); - } + if (int_1 == 1) { + this.mc.displayScreen(this.parent); + } modList.keyPressed(int_1, 0, 0); descriptionListWidget.keyPressed(int_1, 0, 0); } @@ -275,12 +275,23 @@ public void mouseReleased(int mouseX, int mouseY, int mouseButton) { descriptionListWidget.mouseReleased(mouseX, mouseY, mouseButton); } } - + @Override public void render(int mouseX, int mouseY, float delta) { I18n i18n = I18n.getInstance(); - int mouseDX = Mouse.getEventDX() * this.width / this.mc.resolution.getScaledWidthScreenCoords(); // field_6326_c - int mouseDY = this.height - Mouse.getEventDY() * this.height / this.mc.resolution.getScaledHeightScreenCoords() - 1; // field_6325_d + + int dWheel = Mouse.getDWheel(); + if (dWheel != 0) { + double scrollDelta = dWheel > 0 ? 1.0 : -1.0; + if (this.modList != null && this.modList.isMouseOver(mouseX, mouseY)) { + this.modList.mouseScrolled(mouseX, mouseY, scrollDelta); + } else if (this.descriptionListWidget != null && this.descriptionListWidget.isMouseOver(mouseX, mouseY)) { + this.descriptionListWidget.mouseScrolled(mouseX, mouseY, scrollDelta); + } + } + + int mouseDX = Mouse.getDX() * this.width / this.mc.resolution.getScaledWidthScreenCoords(); // field_6326_c + int mouseDY = Mouse.getDY() * this.height / this.mc.resolution.getScaledHeightScreenCoords(); // field_6325_d for (int button = 0; button < Mouse.getButtonCount(); button++) { if (Mouse.isButtonDown(button)) { modList.mouseDragged(mouseX, mouseY, button, mouseDX, mouseDY); @@ -318,8 +329,8 @@ public void render(int mouseX, int mouseY, float delta) { GLRenderer.setColor4f(1, 1, 1, 1); this.selected.bindIconTexture(); - ModListEntry.internalRender(paneY, x); - int lineSpacing = 9 + 1; + ModListEntry.internalRender(paneY, x); + int lineSpacing = 9 + 1; int imageOffset = 36; String name = metadata.getName(); if (name.equals("Minecraft")){ // BAD CODE @@ -328,8 +339,8 @@ public void render(int mouseX, int mouseY, float delta) { name = HardcodedUtil.formatFabricModuleName(name); String trimmedName = name; int maxNameWidth = this.width - (x + imageOffset); - trimmedName = getString(font, name, trimmedName, maxNameWidth); - this.drawStringNoShadow(font, trimmedName, x + imageOffset, paneY + 1, 0xFFFFFF); + trimmedName = getString(font, name, trimmedName, maxNameWidth); + this.drawStringNoShadow(font, trimmedName, x + imageOffset, paneY + 1, 0xFFFFFF); if (mouseX > x + imageOffset && mouseY > paneY + 1 && mouseY < paneY + 1 + 9 && mouseX < x + imageOffset + fontRenderer.stringWidth(trimmedName)) { setTooltip(i18n.translateKeyAndFormat("modmenu.modIdToolTip", metadata.getId())); } @@ -349,10 +360,10 @@ public void render(int mouseX, int mouseY, float delta) { List names = new ArrayList<>(); metadata.getAuthors().stream() - .filter(Objects::nonNull) - .map(Person::getName) - .filter(Objects::nonNull) - .forEach(names::add); + .filter(Objects::nonNull) + .map(Person::getName) + .filter(Objects::nonNull) + .forEach(names::add); if (!names.isEmpty()) { if (names.size() > 1) { @@ -372,19 +383,19 @@ public void render(int mouseX, int mouseY, float delta) { GLRenderer.popFrame(); } - static String getString(FontRenderer fontRenderer, String name, String trimmedName, int maxNameWidth) { - if (fontRenderer.stringWidth(name) > maxNameWidth) { - int maxWidth = maxNameWidth - fontRenderer.stringWidth("..."); - trimmedName = ""; - while (fontRenderer.stringWidth(trimmedName) < maxWidth && trimmedName.length() < name.length()) { - trimmedName += name.charAt(trimmedName.length()); - } - trimmedName = trimmedName.isEmpty() ? "..." : trimmedName.substring(0, trimmedName.length() - 1) + "..."; - } - return trimmedName; - } - - public void overlayBackground(int x1, int y1, int x2, int y2, int red, int green, int blue, int startAlpha, int endAlpha) { + static String getString(FontRenderer fontRenderer, String name, String trimmedName, int maxNameWidth) { + if (fontRenderer.stringWidth(name) > maxNameWidth) { + int maxWidth = maxNameWidth - fontRenderer.stringWidth("..."); + trimmedName = ""; + while (fontRenderer.stringWidth(trimmedName) < maxWidth && trimmedName.length() < name.length()) { + trimmedName += name.charAt(trimmedName.length()); + } + trimmedName = trimmedName.isEmpty() ? "..." : trimmedName.substring(0, trimmedName.length() - 1) + "..."; + } + return trimmedName; + } + + public void overlayBackground(int x1, int y1, int x2, int y2, int red, int green, int blue, int startAlpha, int endAlpha) { GLRenderer.pushFrame(); GLRenderer.setShader(Shaders.INTERFACE); From ac46e10aeac6f10ed263b4e5c3229b376fcbb523 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 00:58:57 +0300 Subject: [PATCH 03/59] Update ModListWidget.java --- .../prospector/modmenu/gui/ModListWidget.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java index 0b2dbc52f..eeeee2339 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java @@ -239,25 +239,34 @@ public void mouseClicked(int double_1, int double_2, int int_1) { if (this.isMouseOver(double_1, double_2)) { ModListEntry entry = this.getEntryAtPos(double_1, double_2); if (entry != null) { - if (entry.list.getFocused() != null) { - if (!entry.list.getFocused().equals(entry)) { - this.setFocused(entry); - this.setSelected(entry); - this.setDragging(true); - super.mouseClicked(double_1, double_2, int_1); - } - } else { - this.setFocused(entry); - this.setSelected(entry); - this.setDragging(true); - super.mouseClicked(double_1, double_2, int_1); - } + if (entry.list.getFocused() != null) { + if (!entry.list.getFocused().equals(entry)) { + this.setFocused(entry); + this.setSelected(entry); + this.setDragging(true); + super.mouseClicked(double_1, double_2, int_1); + } + } else { + this.setFocused(entry); + this.setSelected(entry); + this.setDragging(true); + super.mouseClicked(double_1, double_2, int_1); + } } else if (int_1 == 0) { this.clickedHeader((int) (double_1 - (double) (this.left + this.width / 2 - this.getRowWidth() / 2)), (int) (double_2 - (double) this.top) + (int) this.getScrollAmount() - 4); } } } + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double scrollDelta) { + if (this.isMouseOver(mouseX, mouseY)) { + setScrollAmount(getScrollAmount() - scrollDelta * 20.0); + return true; + } + return false; + } + public final ModListEntry getEntryAtPos(double x, double y) { int int_5 = MathHelper.floor(y - (double) this.top) - this.headerHeight + (int) this.getScrollAmount() - 4; // convertToBlockCoord int index = int_5 / this.itemHeight; From 5cc4e9b42b0ab9ef91ef381711a5979a06728cfb Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 00:59:42 +0300 Subject: [PATCH 04/59] Update EntryListWidget.java --- .../modmenu/gui/EntryListWidget.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java index 4dfdb5788..828c1c164 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java @@ -39,7 +39,7 @@ public abstract class EntryListWidget> extend protected boolean renderSelection = true; protected boolean renderHeader; protected int headerHeight; - private boolean scrolling; + protected boolean scrolling; private E selected; private E focused; @@ -168,7 +168,6 @@ protected void renderDecorations(int i, int j) { } public void render(int i, int j, float f) { - oldY = j; this.renderBackground(); int k = this.getScrollbarPosition(); int l = k + 6; @@ -321,25 +320,25 @@ protected int getScrollbarPosition() { return this.width / 2 + 124; } - @Override + @Override public void mouseClicked(int d, int e, int i) { this.updateScrollingState(d, e, i); if (this.isMouseOver(d, e)) { E entry = this.getEntryAtPosition(d, e); if (entry != null) { - if (entry.list.getFocused() != null) { - if (!entry.list.getFocused().equals(entry)) { - this.focused = entry; - this.selected = entry; - this.dragging = true; - super.mouseClicked(d, e, i); - } - } else { - this.focused = entry; - this.selected = entry; - this.dragging = true; - super.mouseClicked(d, e, i); - } + if (entry.list.getFocused() != null) { + if (!entry.list.getFocused().equals(entry)) { + this.focused = entry; + this.selected = entry; + this.dragging = true; + super.mouseClicked(d, e, i); + } + } else { + this.focused = entry; + this.selected = entry; + this.dragging = true; + super.mouseClicked(d, e, i); + } } else if (i == 0) { this.clickedHeader((int)(d - (double)(this.left + this.width / 2 - this.getRowWidth() / 2)), (int)(e - (double)this.top) + (int)this.getScrollAmount() - 4); } @@ -353,14 +352,12 @@ public boolean mouseReleased(double d, double e, int i) { return false; } - double oldY = -1; public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double mouseDX, double mouseDY) { - if (mouseButton == 0 && isMouseOver(mouseX, mouseY)) { - setScrollAmount(getScrollAmount() - mouseY + oldY); + if (this.scrolling) { + setScrollAmount(getScrollAmount() - mouseDY); return true; - } else { - return false; } + return false; } public boolean mouseScrolled(double d, double e, double f) { From 454d3343063982b2dc8b04e02145e34931dc549f Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:58:13 +0300 Subject: [PATCH 05/59] Update DescriptionListWidget.java --- .../modmenu/gui/DescriptionListWidget.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 24d758deb..97ee27173 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -44,11 +44,20 @@ public void render(int mouseX, int mouseY, float delta) { lastSelected = selectedEntry; clearEntries(); setScrollAmount(-Double.MAX_VALUE); - String description = lastSelected.getMetadata().getDescription(); - String id = lastSelected.getMetadata().getId(); - Collection authors = lastSelected.getMetadata().getAuthors(); - Collection contributors = lastSelected.getMetadata().getContributors(); - Collection licenses = lastSelected.getMetadata().getLicense(); + String id = selectedEntry.getMetadata().getId(); + String descKey = "modmenu.descriptionTranslation." + id; + String translatedDesc = i18n.translateKey(descKey); + + String description; + if (!translatedDesc.equals(descKey)) { + description = translatedDesc; + } else { + description = selectedEntry.getMetadata().getDescription(); + } + + Collection authors = selectedEntry.getMetadata().getAuthors(); + Collection contributors = selectedEntry.getMetadata().getContributors(); + Collection licenses = selectedEntry.getMetadata().getLicense(); if (description.isEmpty() && HardcodedUtil.getHardcodedDescriptions().containsKey(id)) { description = HardcodedUtil.getHardcodedDescription(id); } From a1267461e5a40439b80982ccf4422480accb25d2 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:58:52 +0300 Subject: [PATCH 06/59] Update ModListEntry.java --- .../prospector/modmenu/gui/ModListEntry.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index ccc28f42f..749d0418e 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -14,6 +14,7 @@ import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.renderer.State; import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.core.lang.I18n; import org.lwjgl.opengl.GL11; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,9 +53,15 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou GLRenderer.setColor4f(1, 1, 1, 1); // We LOVE random color calls this.bindIconTexture(); internalRender(y, x); - String name = metadata.getName(); - if (name.equals("Minecraft")){ // BAD CODE - name = "Better than Adventure"; + String id = metadata.getId(); + String translationKey = "modmenu.nameTranslation." + id; + String translatedName = I18n.getInstance().translateKey(translationKey); + String name; + if (!translatedName.equals(translationKey)) { + name = translatedName; + } else { + name = metadata.getName(); + name = HardcodedUtil.formatFabricModuleName(name); } name = HardcodedUtil.formatFabricModuleName(name); String trimmedName = name; @@ -63,9 +70,13 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou trimmedName = ModListScreen.getString(fontRenderer, name, trimmedName, maxNameWidth); this.drawStringNoShadow(font, trimmedName, x + 32 + 3, y + 1, 0xFFFFFF); new BadgeRenderer(client, x + 32 + 3 + font.stringWidth(name) + 2, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); - String description = metadata.getDescription(); - if (description.isEmpty() && HardcodedUtil.getHardcodedDescriptions().containsKey(metadata.getId())) { - description = HardcodedUtil.getHardcodedDescription(metadata.getId()); + String descKey = "modmenu.descriptionTranslation." + id; + String translatedDesc = I18n.getInstance().translateKey(descKey); + String description; + if (!translatedDesc.equals(descKey)) { + description = translatedDesc; + } else { + description = metadata.getDescription(); } RenderUtils.INSTANCE.drawWrappedString(this, description, (x + 32 + 3 + 4), (y + 9 + 2), rowWidth - 32 - 7, 2, 0x808080); From c5010eafd543bedff975b087177d06084c41f30f Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:00:00 +0300 Subject: [PATCH 07/59] Update ModListScreen.java --- .../prospector/modmenu/gui/ModListScreen.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 07366dc38..949ac1d0a 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -279,7 +279,7 @@ public void mouseReleased(int mouseX, int mouseY, int mouseButton) { @Override public void render(int mouseX, int mouseY, float delta) { I18n i18n = I18n.getInstance(); - + int dWheel = Mouse.getDWheel(); if (dWheel != 0) { double scrollDelta = dWheel > 0 ? 1.0 : -1.0; @@ -332,11 +332,18 @@ public void render(int mouseX, int mouseY, float delta) { ModListEntry.internalRender(paneY, x); int lineSpacing = 9 + 1; int imageOffset = 36; - String name = metadata.getName(); - if (name.equals("Minecraft")){ // BAD CODE - name = "Better than Adventure"; + String id = metadata.getId(); + String translationKey = "modmenu.nameTranslation." + id; + String translatedName = i18n.translateKey(translationKey); + String name; + if (!translatedName.equals(translationKey)) { + name = translatedName; + } else { + name = metadata.getName(); + name = HardcodedUtil.formatFabricModuleName(name); } name = HardcodedUtil.formatFabricModuleName(name); + String trimmedName = name; int maxNameWidth = this.width - (x + imageOffset); trimmedName = getString(font, name, trimmedName, maxNameWidth); From f10187bda527bcafbc78bd001a319a6fed00a9d8 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:05:26 +0300 Subject: [PATCH 08/59] Update en_US.lang --- src/main/resources/assets/modmenu/lang/en_US/en_US.lang | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index 899411a92..c58b624c9 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -26,3 +26,12 @@ modmenu.sorting.ascending=A-Z modmenu.sorting.decending=Z-A modmenu.minecraft=Minecraft modmenu.forge=Forge + +modmenu.nameTranslation.minecraft=Better than Adventure! +modmenu.descriptionTranslation.minecraft=The base game. +modmenu.nameTranslation.fabricloader=Fabric Loader +modmenu.descriptionTranslation.fabricloader=The base mod loader. +modmenu.nameTranslation.modmenu=Mod Menu +modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. +modmenu.nameTranslation.java=Java +modmenu.descriptionTranslation.java=The Java runtime environment. From 6cfd00cb08aeb9c2f43c3f969ad9d53df40a93cd Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 27 Sep 2026 23:50:25 +0300 Subject: [PATCH 09/59] Update ModListScreen.java --- .../io/github/prospector/modmenu/gui/ModListScreen.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 949ac1d0a..cd576d17b 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -17,6 +17,7 @@ import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.renderer.State; import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.client.util.helper.UrlHelper; import net.minecraft.core.Global; import net.minecraft.core.lang.I18n; import org.lwjgl.Sys; @@ -207,13 +208,11 @@ protected void buttonClicked(ButtonElement button) { break; } case WEBSITE_BUTTON_ID: { - final ModMetadata metadata = Objects.requireNonNull(selected).getMetadata(); - metadata.getContact().get("homepage").ifPresent(Sys::openURL); + Objects.requireNonNull(selected).getMetadata().getContact().get("homepage").ifPresent(UrlHelper::openURL); break; } case ISSUES_BUTTON_ID: { - final ModMetadata metadata = Objects.requireNonNull(selected).getMetadata(); - metadata.getContact().get("issues").ifPresent(Sys::openURL); + Objects.requireNonNull(selected).getMetadata().getContact().get("issues").ifPresent(UrlHelper::openURL); break; } case TOGGLE_FILTER_OPTIONS_BUTTON_ID: { From bb6fde283d8618c6a4ea7590f11bb02f80d26317 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 28 Sep 2026 10:33:36 +0300 Subject: [PATCH 10/59] Update HardcodedUtil.java --- .../java/io/github/prospector/modmenu/util/HardcodedUtil.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java b/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java index ca10db64e..d9a753f43 100644 --- a/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java @@ -15,10 +15,6 @@ public final class HardcodedUtil { private static final Set FABRIC_MODS = new HashSet<>(); private static final HashMap HARDCODED_DESCRIPTIONS = new HashMap<>(); - public static void initializeHardcodings() { - HARDCODED_DESCRIPTIONS.put("minecraft", new Random().nextInt(1000) == 0 ? "The based game." : "The base game."); - } - public static void hardcodeModuleMetadata(ModContainer mod, ModMetadata metadata, String id) { Matcher matcher = FABRIC_PATTERN.matcher(id); if (matcher.matches() || id.equals("fabric-api-base") || id.equals("fabric-renderer-indigo")) { From 2648d1ad85ae3e9bea5508dc788c7d77834fb580 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:30:23 +0300 Subject: [PATCH 11/59] Update ModListScreen.java --- .../io/github/prospector/modmenu/gui/ModListScreen.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index cd576d17b..13b04e3d5 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -355,12 +355,9 @@ public void render(int mouseX, int mouseY, float delta) { init = false; } badgeRenderer.draw(mouseX, mouseY); - String versionString; - if (metadata.getName().equals("Minecraft")){ // BAD CODE - versionString = Global.VERSION; - } else { - versionString = metadata.getVersion().getFriendlyString(); - } + String versionString = metadata.getId().equals("minecraft") + ? Global.VERSION + : metadata.getVersion().getFriendlyString(); this.drawStringNoShadow(font, "v" + versionString, x + imageOffset, paneY + 2 + lineSpacing, 0x808080); String authors; List names = new ArrayList<>(); From df9918ac1883d7efe88070487ca74eecf07a0b4f Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:14:16 +0300 Subject: [PATCH 12/59] Update DescriptionListWidget.java --- .../github/prospector/modmenu/gui/DescriptionListWidget.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 97ee27173..2a16d0aa1 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -58,9 +58,6 @@ public void render(int mouseX, int mouseY, float delta) { Collection authors = selectedEntry.getMetadata().getAuthors(); Collection contributors = selectedEntry.getMetadata().getContributors(); Collection licenses = selectedEntry.getMetadata().getLicense(); - if (description.isEmpty() && HardcodedUtil.getHardcodedDescriptions().containsKey(id)) { - description = HardcodedUtil.getHardcodedDescription(id); - } if (lastSelected != null && description != null && !description.isEmpty()) { for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, description.replaceAll("\n", "\n\n"), getRowWidth())) { children().add(new DescriptionEntry(line)); From 6ace480a828291b2d650836e3ac4502bdb499396 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:14:57 +0300 Subject: [PATCH 13/59] Update HardcodedUtil.java --- .../io/github/prospector/modmenu/util/HardcodedUtil.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java b/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java index d9a753f43..983adf5e2 100644 --- a/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java @@ -13,7 +13,6 @@ public final class HardcodedUtil { private static final Pattern FABRIC_PATTERN = Pattern.compile("^fabric-.*(-v\\d+)$"); private static final Set FABRIC_MODS = new HashSet<>(); - private static final HashMap HARDCODED_DESCRIPTIONS = new HashMap<>(); public static void hardcodeModuleMetadata(ModContainer mod, ModMetadata metadata, String id) { Matcher matcher = FABRIC_PATTERN.matcher(id); @@ -65,15 +64,7 @@ private static String capitalize(String str) { return sb.toString(); } - public static String getHardcodedDescription(String id) { - return HARDCODED_DESCRIPTIONS.getOrDefault(id, ""); - } - public static Set getFabricMods() { return FABRIC_MODS; } - - public static HashMap getHardcodedDescriptions() { - return HARDCODED_DESCRIPTIONS; - } } From d105283e80037060ff4cf73a81432e7390d93cfe Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 3 Oct 2026 23:18:54 +0300 Subject: [PATCH 14/59] Update DescriptionListWidget.java --- .../modmenu/gui/DescriptionListWidget.java | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 2a16d0aa1..d6bcddc4c 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -1,14 +1,18 @@ package io.github.prospector.modmenu.gui; -import io.github.prospector.modmenu.util.HardcodedUtil; import io.github.prospector.modmenu.util.RenderUtils; +import net.fabricmc.loader.api.metadata.ContactInformation; import net.fabricmc.loader.api.metadata.Person; import net.minecraft.client.Minecraft; import net.minecraft.client.render.font.FontRenderer; +import net.minecraft.client.util.helper.UrlHelper; import net.minecraft.core.lang.I18n; +import net.minecraft.core.net.command.TextFormatting; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; public class DescriptionListWidget extends EntryListWidget { private final ModListScreen parent; @@ -36,6 +40,37 @@ protected int getScrollbarPosition() { return this.width - 6 + left; } + protected class LinkEntry extends DescriptionEntry { + private final String link; + private final int indent; + private int lastRenderX; + private int lastRenderY; + + public LinkEntry(String text, String link, int indent) { + super(text); + this.link = link; + this.indent = indent; + } + + @Override + public void render(int index, int y, int x, int itemWidth, int itemHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + this.lastRenderX = x + indent; + this.lastRenderY = y; + + String formattedText = TextFormatting.formatted(text, TextFormatting.BLUE, TextFormatting.UNDERLINE); + this.drawStringShadow(this.fontRenderer, formattedText, lastRenderX, lastRenderY, 0xFFFFFF); + } + + public void mouseClicked(int mouseX, int mouseY, int button) { + if (button == 0) { + int textWidth = fontRenderer.stringWidth(text); + if (mouseX >= lastRenderX && mouseX <= lastRenderX + textWidth && mouseY >= lastRenderY && mouseY <= lastRenderY + 12) { + UrlHelper.openURL(link); + } + } + } + } + @Override public void render(int mouseX, int mouseY, float delta) { I18n i18n = I18n.getInstance(); @@ -58,11 +93,13 @@ public void render(int mouseX, int mouseY, float delta) { Collection authors = selectedEntry.getMetadata().getAuthors(); Collection contributors = selectedEntry.getMetadata().getContributors(); Collection licenses = selectedEntry.getMetadata().getLicense(); + if (lastSelected != null && description != null && !description.isEmpty()) { for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, description.replaceAll("\n", "\n\n"), getRowWidth())) { children().add(new DescriptionEntry(line)); } } + if (!authors.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.authors"))); @@ -70,6 +107,7 @@ public void render(int mouseX, int mouseY, float delta) { children().add(new DescriptionEntry(" " + person.getName())); } } + if (!contributors.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.contributors"))); @@ -77,6 +115,36 @@ public void render(int mouseX, int mouseY, float delta) { children().add(new DescriptionEntry(" " + person.getName())); } } + + ContactInformation contact = selectedEntry.getMetadata().getContact(); + Map links = new HashMap<>(contact.asMap()); + + links.remove("homepage"); + links.remove("issues"); + + if (!links.isEmpty()) { + children().add(new DescriptionEntry("")); + + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, i18n.translateKey("modmenu.links"), getRowWidth())) { + children().add(new DescriptionEntry(line)); + } + + links.forEach((key, value) -> { + int indent = 8; + String translationKey = "modmenu." + key; + String translatedKey = i18n.translateKey(translationKey); + + if (translatedKey.equals(translationKey)) { + translatedKey = Character.toUpperCase(key.charAt(0)) + key.substring(1); + } + + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, translatedKey, getRowWidth() - 16)) { + children().add(new LinkEntry(line, value, indent)); + indent = 16; + } + }); + } + if (!licenses.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.licenses"))); @@ -88,6 +156,16 @@ public void render(int mouseX, int mouseY, float delta) { super.render(mouseX, mouseY, delta); } + @Override + public void mouseClicked(int mouseX, int mouseY, int button) { + super.mouseClicked(mouseX, mouseY, button); + for (DescriptionEntry entry : children()) { + if (entry instanceof LinkEntry) { + entry.mouseClicked(mouseX, mouseY, button); + } + } + } + @Override protected void renderHoleBackground(int y1, int y2, int startAlpha, int endAlpha) { // Awful hack but it makes the background "seamless" From eabac7f9ccdb5bfa98d5780427a7ac6c357621e0 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 3 Oct 2026 23:20:11 +0300 Subject: [PATCH 15/59] Update en_US.lang --- .../assets/modmenu/lang/en_US/en_US.lang | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index c58b624c9..d4fd3cdcb 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -27,6 +27,31 @@ modmenu.sorting.decending=Z-A modmenu.minecraft=Minecraft modmenu.forge=Forge +modmenu.links=Links: +modmenu.sources=Source +modmenu.bluesky=Bluesky +modmenu.buymeacoffee=Buy Me a Coffee +modmenu.coindrop=Coindrop +modmenu.crowdin=Crowdin +modmenu.curseforge=CurseForge +modmenu.discord=Discord +modmenu.donate=Donate +modmenu.flattr=Flattr +modmenu.github_releases=GitHub Releases +modmenu.github_sponsors=GitHub Sponsors +modmenu.kofi=Ko-fi +modmenu.liberapay=Liberapay +modmenu.mastodon=Mastodon +modmenu.modrinth=Modrinth +modmenu.opencollective=Open Collective +modmenu.patreon=Patreon +modmenu.paypal=PayPal +modmenu.reddit=Reddit +modmenu.twitch=Twitch +modmenu.wiki=Wiki +modmenu.twitter=X (Twitter) +modmenu.youtube=YouTube + modmenu.nameTranslation.minecraft=Better than Adventure! modmenu.descriptionTranslation.minecraft=The base game. modmenu.nameTranslation.fabricloader=Fabric Loader From 594828d67ce96360e57d4464e47f32d8d22217df Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 12:17:52 +0300 Subject: [PATCH 16/59] Update ModListScreen.java --- .../github/prospector/modmenu/gui/ModListScreen.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 13b04e3d5..a58422a36 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -17,6 +17,7 @@ import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.renderer.State; import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.client.util.helper.FileOpener; import net.minecraft.client.util.helper.UrlHelper; import net.minecraft.core.Global; import net.minecraft.core.lang.I18n; @@ -231,10 +232,13 @@ protected void buttonClicked(ButtonElement button) { } case MODS_FOLDER_BUTTON_ID: { File modsFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "mods"); + if (!modsFolder.exists()) { + modsFolder.mkdirs(); + } try { - Sys.openURL(modsFolder.toURI().toURL().toString()); - } catch (MalformedURLException e) { - LOGGER.error("Malformed mods folder URL", e); + FileOpener.open(modsFolder); + } catch (Exception e) { + LOGGER.error("Failed to open mods folder", e); } break; } From 1a7f705c2966ffae7bdf8e6db5035c362bf783fa Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 14:11:31 +0300 Subject: [PATCH 17/59] remove harcoded util --- .../io/github/prospector/modmenu/ModMenu.java | 4 -- .../modmenu/config/ModMenuConfig.java | 6 +- .../prospector/modmenu/gui/ModListEntry.java | 7 +- .../prospector/modmenu/gui/ModListScreen.java | 5 -- .../modmenu/util/BadgeRenderer.java | 10 +-- .../modmenu/util/HardcodedUtil.java | 70 ------------------- .../modmenu/util/ModListSearch.java | 22 ++++-- .../assets/modmenu/lang/en_US/en_US.lang | 21 ++++-- 8 files changed, 38 insertions(+), 107 deletions(-) delete mode 100644 src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 69c195d7d..0c2a9b8be 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -6,7 +6,6 @@ import com.google.gson.GsonBuilder; import io.github.prospector.modmenu.api.ModMenuApi; import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.util.HardcodedUtil; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; @@ -88,7 +87,6 @@ public void onInitialize() { }); configScreenFactories.put("minecraft", (screenBase -> new ScreenOptions(screenBase, OptionsPages.GENERAL))); Collection mods = FabricLoader.getInstance().getAllMods(); - HardcodedUtil.initializeHardcodings(); for (ModContainer mod : mods) { ModMetadata metadata = mod.getMetadata(); String id = metadata.getId(); @@ -129,8 +127,6 @@ public void onInitialize() { list.add(mod); }); } - } else { - HardcodedUtil.hardcodeModuleMetadata(mod, metadata, id); } } } diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 128a3df5d..172a92611 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -1,7 +1,5 @@ package io.github.prospector.modmenu.config; - -import io.github.prospector.modmenu.util.HardcodedUtil; import net.fabricmc.loader.api.ModContainer; import net.minecraft.core.lang.I18n; @@ -30,8 +28,8 @@ public Sorting getSorting() { } public enum Sorting { - ASCENDING(Comparator.comparing(modContainer -> HardcodedUtil.formatFabricModuleName(modContainer.getMetadata().getName())), "modmenu.sorting.ascending"), - DECENDING(ASCENDING.getComparator().reversed(), "modmenu.sorting.decending"); + ASCENDING(Comparator.comparing(modContainer -> modContainer.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER),"modmenu.sorting.ascending"), + DESCENDING(ASCENDING.getComparator().reversed(),"modmenu.sorting.decending"); final Comparator comparator; final String key; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 749d0418e..42612eec4 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -3,7 +3,6 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.util.BadgeRenderer; -import io.github.prospector.modmenu.util.HardcodedUtil; import io.github.prospector.modmenu.util.RenderUtils; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; @@ -61,9 +60,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou name = translatedName; } else { name = metadata.getName(); - name = HardcodedUtil.formatFabricModuleName(name); } - name = HardcodedUtil.formatFabricModuleName(name); String trimmedName = name; int maxNameWidth = rowWidth - 32 - 3; FontRenderer font = this.fontRenderer; @@ -108,9 +105,7 @@ private BufferedImage createIcon() { } if (!Files.exists(path)) { ModContainer modMenu = FabricLoader.getInstance().getModContainer(ModMenu.MOD_ID).orElseThrow(IllegalAccessError::new); - if (HardcodedUtil.getFabricMods().contains(metadata.getId())) { - path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/fabric_icon.png"); - } else if (metadata.getId().equals("minecraft")) { + if (metadata.getId().equals("minecraft")) { path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/mc_icon.png"); } else if (metadata.getId().equals("java")) { path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/java_icon.png"); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index a58422a36..71a05283a 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -4,7 +4,6 @@ import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.BadgeRenderer; import io.github.prospector.modmenu.util.ButtonUtil; -import io.github.prospector.modmenu.util.HardcodedUtil; import io.github.prospector.modmenu.util.RenderUtils; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -21,7 +20,6 @@ import net.minecraft.client.util.helper.UrlHelper; import net.minecraft.core.Global; import net.minecraft.core.lang.I18n; -import org.lwjgl.Sys; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL41; @@ -30,7 +28,6 @@ import org.spongepowered.include.com.google.common.base.Joiner; import java.io.File; -import java.net.MalformedURLException; import java.text.NumberFormat; import java.util.*; @@ -343,9 +340,7 @@ public void render(int mouseX, int mouseY, float delta) { name = translatedName; } else { name = metadata.getName(); - name = HardcodedUtil.formatFabricModuleName(name); } - name = HardcodedUtil.formatFabricModuleName(name); String trimmedName = name; int maxNameWidth = this.width - (x + imageOffset); diff --git a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java index a19b76738..97b0bbe57 100644 --- a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java +++ b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java @@ -33,19 +33,19 @@ public void draw(int mouseX, int mouseY) { this.badgeX = startX; this.badgeY = startY; if (ModMenu.LIBRARY_MODS.contains(metadata.getId())) { - drawBadge(i18n.translateKey("modmenu.library"), 0x8810d098, 0x88046146, mouseX, mouseY); + drawBadge(i18n.translateKey("modmenu.badge.library"), 0x8810d098, 0x88046146, mouseX, mouseY); } if (ModMenu.CLIENTSIDE_MODS.contains(metadata.getId())) { - drawBadge(i18n.translateKey("modmenu.clientsideOnly"), 0x884383E3, 0x880E4699, mouseX, mouseY); + drawBadge(i18n.translateKey("modmenu.badge.clientsideOnly"), 0x884383E3, 0x880E4699, mouseX, mouseY); } if (ModMenu.DEPRECATED_MODS.contains(metadata.getId())) { - drawBadge(i18n.translateKey("modmenu.deprecated"), 0xFF841426, 0xFF530C17, mouseX, mouseY); + drawBadge(i18n.translateKey("modmenu.badge.deprecated"), 0xFF841426, 0xFF530C17, mouseX, mouseY); } if (ModMenu.PATCHWORK_FORGE_MODS.contains(metadata.getId())) { - drawBadge(i18n.translateKey("modmenu.forge"), 0x887C89A3, 0x88202C43, mouseX, mouseY); + drawBadge(i18n.translateKey("modmenu.badge.forge"), 0x887C89A3, 0x88202C43, mouseX, mouseY); } if (metadata.getId().equals("minecraft")) { - drawBadge(i18n.translateKey("modmenu.minecraft"), 0x88BCBCBC, 0x88535353, mouseX, mouseY); + drawBadge(i18n.translateKey("modmenu.badge.minecraft"), 0x88BCBCBC, 0x88535353, mouseX, mouseY); } if (ModMenu.CUSTOM_BADGE_MODS.containsKey(metadata.getId())) { Map> map = ModMenu.CUSTOM_BADGE_MODS.get(metadata.getId()); diff --git a/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java b/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java deleted file mode 100644 index 983adf5e2..000000000 --- a/src/main/java/io/github/prospector/modmenu/util/HardcodedUtil.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.prospector.modmenu.util; - - -import io.github.prospector.modmenu.ModMenu; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.api.metadata.ModMetadata; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class HardcodedUtil { - private static final Pattern FABRIC_PATTERN = Pattern.compile("^fabric-.*(-v\\d+)$"); - private static final Set FABRIC_MODS = new HashSet<>(); - - public static void hardcodeModuleMetadata(ModContainer mod, ModMetadata metadata, String id) { - Matcher matcher = FABRIC_PATTERN.matcher(id); - if (matcher.matches() || id.equals("fabric-api-base") || id.equals("fabric-renderer-indigo")) { - FABRIC_MODS.add(id); - if (FabricLoader.getInstance().isModLoaded("fabric")) { - Optional parent = FabricLoader.getInstance().getModContainer("fabric"); - parent.ifPresent(modContainer -> { - final List list = ModMenu.PARENT_MAP.computeIfAbsent(modContainer, k -> new ArrayList<>()); - list.add(mod); - });; - } - ModMenu.addLibraryMod(id); - if (id.equals("fabric-keybindings-v0") || id.equals("fabric-models-v0") || id.equals("fabric-renderer-api-v1") || id.equals("fabric-renderer-indigo") || id.equals("fabric-rendering-fluids-v1") || id.equals("fabric-rendering-v0") || id.equals("fabric-textures-v0")) { - ModMenu.CLIENTSIDE_MODS.add(id); - } - } - if (id.equals("fabricloader") || id.equals("fabric") || metadata.getName().endsWith(" API")) { - ModMenu.addLibraryMod(id); - } - } - - public static String formatFabricModuleName(String name) { - Matcher matcher = FABRIC_PATTERN.matcher(name); - if (matcher.matches() || name.equals("fabric-renderer-indigo") || name.equals("fabric-api-base")) { - if (matcher.matches()) { - String v = matcher.group(1); - name = capitalize(name.replace(v, "").replace("-", " ")); - name = name + " (" + v.replace("-", "") + ")"; - } else { - name = capitalize(name.replace("-", " ")); - } - name = name.replace("Api", "API"); - name = name.replace("Blockentity", "BlockEntity"); - } - return name; - } - - private static String capitalize(String str) { - StringBuilder sb = new StringBuilder(); - boolean capitalizeNext = true; - for (int i = 0; i < str.length(); i++) { - char c = str.charAt(i); - if (capitalizeNext) - c = Character.toTitleCase(c); - capitalizeNext = Character.isWhitespace(c); - sb.append(c); - } - return sb.toString(); - } - - public static Set getFabricMods() { - return FABRIC_MODS; - } -} diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index a21e9e567..c12f70d03 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -6,6 +6,7 @@ import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.Person; +import net.minecraft.core.lang.I18n; import java.util.List; import java.util.Locale; @@ -33,13 +34,20 @@ private static boolean passesFilters(ModListScreen screen, ModContainer containe //Some basic search, could do with something more advanced but this will do for now - if (HardcodedUtil.formatFabricModuleName(metadata.getName()).toLowerCase(Locale.ROOT).contains(query) //Search mod name - || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod name - || authorMatches(container, query) //Search via author - || (ModMenu.LIBRARY_MODS.contains(modId) && "api library".contains(query)) //Search for lib mods - || ("clientside".contains(query) && ModMenu.CLIENTSIDE_MODS.contains(modId)) //Search for clientside mods - || ("deprecated".contains(query) && ModMenu.DEPRECATED_MODS.contains(modId)) //Search for clientside mods - || ("configurations configs configures configurable".contains(query) && ModMenu.hasConfigScreenFactory(modId)) //Search for mods that can be configured + I18n i18n = I18n.getInstance(); + String translationKey = "modmenu.nameTranslation." + modId; + String translatedName = I18n.getInstance().translateKey(translationKey); + + String modName = !translatedName.equals(translationKey) ? translatedName : metadata.getName(); + + if (modName.toLowerCase(Locale.ROOT).contains(query) // Search mod name + || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod name + || authorMatches(container, query) // Search via author + || (ModMenu.LIBRARY_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.library").toLowerCase(Locale.ROOT).contains(query)) // Search for lib mods + || (ModMenu.CLIENTSIDE_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.clientside").toLowerCase(Locale.ROOT).contains(query)) // Search for clientside mods + || (ModMenu.DEPRECATED_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.deprecated").toLowerCase(Locale.ROOT).contains(query)) // Search for deprecated mods + || (ModMenu.PATCHWORK_FORGE_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.patchwork").toLowerCase(Locale.ROOT).contains(query)) // Search for forge and patchwork + || (ModMenu.hasConfigScreenFactory(modId) && i18n.translateKey("modmenu.searchTerms.configurable").toLowerCase(Locale.ROOT).contains(query)) // Search for mods that can be configured ) { return true; } diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index d4fd3cdcb..5f5ef371a 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -13,9 +13,6 @@ modmenu.authors=Authors: modmenu.contributors=Contributors: modmenu.licenses=Licenses: modmenu.search=Search for mods -modmenu.clientsideOnly=Client -modmenu.library=Library -modmenu.deprecated=Deprecated modmenu.toggleFilterOptions=Toggle Filter Options modmenu.showLibraries=Libraries: %s modmenu.showLibraries.true=Shown @@ -24,8 +21,20 @@ modmenu.showingMods=Showing %s Mods modmenu.sorting=Sort: %s modmenu.sorting.ascending=A-Z modmenu.sorting.decending=Z-A -modmenu.minecraft=Minecraft -modmenu.forge=Forge + +modmenu.searchTerms.library=api library +modmenu.searchTerms.patchwork=patchwork forge fml +modmenu.searchTerms.modpack=modpack pack +modmenu.searchTerms.deprecated=deprecated outdated old +modmenu.searchTerms.clientside=clientside gameside +modmenu.searchTerms.configurable=configurations configs configures configurable options settings +modmenu.searchTerms.hasUpdate=updates version + +modmenu.badge.clientsideOnly=Client +modmenu.badge.library=Library +modmenu.badge.deprecated=Deprecated +modmenu.badge.minecraft=Minecraft +modmenu.badge.forge=Forge modmenu.links=Links: modmenu.sources=Source @@ -59,4 +68,4 @@ modmenu.descriptionTranslation.fabricloader=The base mod loader. modmenu.nameTranslation.modmenu=Mod Menu modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. modmenu.nameTranslation.java=Java -modmenu.descriptionTranslation.java=The Java runtime environment. +modmenu.descriptionTranslation.java=The Java runtime environment. \ No newline at end of file From 83b4a6f63845703575d131f9f1d890e8851eb5cc Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 15:47:35 +0300 Subject: [PATCH 18/59] Update en_US.lang --- src/main/resources/assets/modmenu/lang/en_US/en_US.lang | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index 5f5ef371a..eab3e7b76 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -23,12 +23,10 @@ modmenu.sorting.ascending=A-Z modmenu.sorting.decending=Z-A modmenu.searchTerms.library=api library -modmenu.searchTerms.patchwork=patchwork forge fml -modmenu.searchTerms.modpack=modpack pack -modmenu.searchTerms.deprecated=deprecated outdated old modmenu.searchTerms.clientside=clientside gameside +modmenu.searchTerms.deprecated=deprecated outdated old +modmenu.searchTerms.patchwork=patchwork forge fml modmenu.searchTerms.configurable=configurations configs configures configurable options settings -modmenu.searchTerms.hasUpdate=updates version modmenu.badge.clientsideOnly=Client modmenu.badge.library=Library @@ -68,4 +66,4 @@ modmenu.descriptionTranslation.fabricloader=The base mod loader. modmenu.nameTranslation.modmenu=Mod Menu modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. modmenu.nameTranslation.java=Java -modmenu.descriptionTranslation.java=The Java runtime environment. \ No newline at end of file +modmenu.descriptionTranslation.java=The Java runtime environment. From 2f6d126cd280782b26f38ffc508c13f2066a96df Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 18:30:31 +0300 Subject: [PATCH 19/59] Update DescriptionListWidget.java --- .../modmenu/gui/DescriptionListWidget.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index d6bcddc4c..12e2065d0 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -90,16 +90,29 @@ public void render(int mouseX, int mouseY, float delta) { description = selectedEntry.getMetadata().getDescription(); } - Collection authors = selectedEntry.getMetadata().getAuthors(); - Collection contributors = selectedEntry.getMetadata().getContributors(); - Collection licenses = selectedEntry.getMetadata().getLicense(); - + if ("java".equals(id)) { + String vendor = System.getProperty("java.vendor", "Unknown Vendor"); + String javaDist = i18n.translateKeyAndFormat("modmenu.javaDistributionName", vendor); + description = (description != null ? description : "") + "\n" + javaDist; + } + if (lastSelected != null && description != null && !description.isEmpty()) { - for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, description.replaceAll("\n", "\n\n"), getRowWidth())) { - children().add(new DescriptionEntry(line)); + String[] paragraphs = description.split("\r?\n"); + for (String paragraph : paragraphs) { + if (paragraph.isEmpty()) { + children().add(new DescriptionEntry("")); + continue; + } + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, paragraph, getRowWidth())) { + children().add(new DescriptionEntry(line)); + } } } - + + Collection authors = selectedEntry.getMetadata().getAuthors(); + Collection contributors = selectedEntry.getMetadata().getContributors(); + Collection licenses = selectedEntry.getMetadata().getLicense(); + if (!authors.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.authors"))); @@ -107,7 +120,7 @@ public void render(int mouseX, int mouseY, float delta) { children().add(new DescriptionEntry(" " + person.getName())); } } - + if (!contributors.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.contributors"))); @@ -115,7 +128,7 @@ public void render(int mouseX, int mouseY, float delta) { children().add(new DescriptionEntry(" " + person.getName())); } } - + ContactInformation contact = selectedEntry.getMetadata().getContact(); Map links = new HashMap<>(contact.asMap()); @@ -144,7 +157,7 @@ public void render(int mouseX, int mouseY, float delta) { } }); } - + if (!licenses.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.licenses"))); From 41adc87d25fd65e22f5b826b1399c8af342bdaf0 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 18:31:28 +0300 Subject: [PATCH 20/59] Update ModListEntry.java --- .../prospector/modmenu/gui/ModListEntry.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 42612eec4..81427ecc8 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -44,6 +44,7 @@ public ModListEntry(Minecraft mc, ModContainer container, ModListWidget list) { @Override public void render(int index, int y, int x, int rowWidth, int rowHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + I18n i18n = I18n.getInstance(); x += getXOffset(); rowWidth -= getXOffset(); @@ -52,9 +53,10 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou GLRenderer.setColor4f(1, 1, 1, 1); // We LOVE random color calls this.bindIconTexture(); internalRender(y, x); + String id = metadata.getId(); String translationKey = "modmenu.nameTranslation." + id; - String translatedName = I18n.getInstance().translateKey(translationKey); + String translatedName = i18n.translateKey(translationKey); String name; if (!translatedName.equals(translationKey)) { name = translatedName; @@ -67,15 +69,30 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou trimmedName = ModListScreen.getString(fontRenderer, name, trimmedName, maxNameWidth); this.drawStringNoShadow(font, trimmedName, x + 32 + 3, y + 1, 0xFFFFFF); new BadgeRenderer(client, x + 32 + 3 + font.stringWidth(name) + 2, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); + String descKey = "modmenu.descriptionTranslation." + id; - String translatedDesc = I18n.getInstance().translateKey(descKey); + String translatedDesc = i18n.translateKey(descKey); String description; if (!translatedDesc.equals(descKey)) { description = translatedDesc; } else { description = metadata.getDescription(); } - RenderUtils.INSTANCE.drawWrappedString(this, description, (x + 32 + 3 + 4), (y + 9 + 2), rowWidth - 32 - 7, 2, 0x808080); + + if ("java".equals(id)) { + String vendor = System.getProperty("java.vendor", "Unknown Vendor"); + String javaDist = i18n.translateKeyAndFormat("modmenu.javaDistributionName", vendor); + description = (description != null ? description : "") + " " + javaDist; + } + + if (description != null) { + String cleanDescription = description.replaceAll("\r?\n", " "); + int textX = x + 32 + 3 + 4; + int textY = y + 9 + 2; + int maxWidth = rowWidth - 32 - 7; + + RenderUtils.INSTANCE.drawWrappedString(this, cleanDescription, textX, textY, maxWidth, 2, 0x808080); + } GLRenderer.popFrame(); } From c8be18f8ac15efee9f98c3de08458f9543099e41 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 18:49:13 +0300 Subject: [PATCH 21/59] Update ModMenuConfig.java --- .../java/io/github/prospector/modmenu/config/ModMenuConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 172a92611..4a40bc91d 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -1,5 +1,6 @@ package io.github.prospector.modmenu.config; + import net.fabricmc.loader.api.ModContainer; import net.minecraft.core.lang.I18n; From f21ff5bd2913467636f6dfada2191d131486e6eb Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 18:51:03 +0300 Subject: [PATCH 22/59] Update ModListSearch.java --- .../java/io/github/prospector/modmenu/util/ModListSearch.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index c12f70d03..b7942b1e7 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -32,14 +32,12 @@ private static boolean passesFilters(ModListScreen screen, ModContainer containe ModMetadata metadata = container.getMetadata(); String modId = metadata.getId(); - //Some basic search, could do with something more advanced but this will do for now I18n i18n = I18n.getInstance(); String translationKey = "modmenu.nameTranslation." + modId; String translatedName = I18n.getInstance().translateKey(translationKey); - String modName = !translatedName.equals(translationKey) ? translatedName : metadata.getName(); - + if (modName.toLowerCase(Locale.ROOT).contains(query) // Search mod name || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod name || authorMatches(container, query) // Search via author From 72f175f786f3c50f4822c2c6e3605cc0fb0ac070 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 19:04:22 +0300 Subject: [PATCH 23/59] Update ModListEntry.java --- .../prospector/modmenu/gui/ModListEntry.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 81427ecc8..2486ae2f9 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -82,16 +82,29 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou if ("java".equals(id)) { String vendor = System.getProperty("java.vendor", "Unknown Vendor"); String javaDist = i18n.translateKeyAndFormat("modmenu.javaDistributionName", vendor); - description = (description != null ? description : "") + " " + javaDist; + description = (description != null ? description : "") + "\n" + javaDist; } if (description != null) { - String cleanDescription = description.replaceAll("\r?\n", " "); int textX = x + 32 + 3 + 4; int textY = y + 9 + 2; int maxWidth = rowWidth - 32 - 7; - - RenderUtils.INSTANCE.drawWrappedString(this, cleanDescription, textX, textY, maxWidth, 2, 0x808080); + int maxLines = 2; + int currentLine = 0; + + font = this.fontRenderer; + String[] paragraphs = description.split("\r?\n"); + + for (String paragraph : paragraphs) { + if (currentLine >= maxLines) break; + + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(font, paragraph, maxWidth)) { + if (currentLine >= maxLines) break; + + this.drawStringNoShadow(font, line, textX, textY + (currentLine * 9), 0x808080); + currentLine++; + } + } } GLRenderer.popFrame(); From 7bbaffeea07cc0b8b36b1c04b43b5cd35be4d288 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 19:05:34 +0300 Subject: [PATCH 24/59] Update ModMenu.java --- src/main/java/io/github/prospector/modmenu/ModMenu.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 0c2a9b8be..8d2e42bfb 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -128,6 +128,14 @@ public void onInitialize() { }); } } + + // Hardcode marking as library + if (id.equals("fabricloader") + || id.equals("fabric") + || id.equals("java") + || id.equals("mixinextras")) { + addLibraryMod(id); + } } } From 78a0e6a5637df00e0b6a68383e2bb3a963ad075b Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 4 Oct 2026 19:06:57 +0300 Subject: [PATCH 25/59] Update en_US.lang --- src/main/resources/assets/modmenu/lang/en_US/en_US.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index eab3e7b76..5c3819069 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -67,3 +67,4 @@ modmenu.nameTranslation.modmenu=Mod Menu modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. modmenu.nameTranslation.java=Java modmenu.descriptionTranslation.java=The Java runtime environment. +modmenu.javaDistributionName=Running: %s From e327f4d125ada041ada2d991997b99dfc8be9f8e Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:14:54 +0300 Subject: [PATCH 26/59] Update ModMenuConfig.java --- .../java/io/github/prospector/modmenu/config/ModMenuConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 4a40bc91d..c0d4351b2 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -30,7 +30,7 @@ public Sorting getSorting() { public enum Sorting { ASCENDING(Comparator.comparing(modContainer -> modContainer.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER),"modmenu.sorting.ascending"), - DESCENDING(ASCENDING.getComparator().reversed(),"modmenu.sorting.decending"); + DESCENDING(ASCENDING.getComparator().reversed(),"modmenu.sorting.descending"); final Comparator comparator; final String key; From 858691719d9f087a227dd3b471d40d0b2c41c160 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:15:15 +0300 Subject: [PATCH 27/59] Update en_US.lang --- src/main/resources/assets/modmenu/lang/en_US/en_US.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index 5c3819069..774002d20 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -20,7 +20,7 @@ modmenu.showLibraries.false=Hidden modmenu.showingMods=Showing %s Mods modmenu.sorting=Sort: %s modmenu.sorting.ascending=A-Z -modmenu.sorting.decending=Z-A +modmenu.sorting.descending=Z-A modmenu.searchTerms.library=api library modmenu.searchTerms.clientside=clientside gameside From cbc3647246b3d070f98ab0fbade181df4e0e6649 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:08:20 +0300 Subject: [PATCH 28/59] remove unique badge --- .../prospector/modmenu/impl/ModMenuApiImpl.java | 17 ----------------- src/main/resources/fabric.mod.json | 3 --- 2 files changed, 20 deletions(-) delete mode 100644 src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java diff --git a/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java b/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java deleted file mode 100644 index 4496da662..000000000 --- a/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.prospector.modmenu.impl; - - -import io.github.prospector.modmenu.api.ModMenuApi; -import io.github.prospector.modmenu.util.TriConsumer; - -public class ModMenuApiImpl implements ModMenuApi { - @Override - public String getModId() { - return "modmenu"; - } - - @Override - public void attachCustomBadges(TriConsumer consumer) { - consumer.accept("Mod Menu", 0xff7a2b7c, 0xff510d54); - } -} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index db1082c74..0175450ee 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -9,9 +9,6 @@ "entrypoints": { "main": [ "io.github.prospector.modmenu.ModMenu" - ], - "modmenu": [ - "io.github.prospector.modmenu.impl.ModMenuApiImpl" ] }, "contact": { From eec369ae09233022170b89244781fce7b8d36cfd Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:27:20 +0300 Subject: [PATCH 29/59] Search mod description --- .../prospector/modmenu/gui/ModListScreen.java | 11 +++---- .../modmenu/util/ModListSearch.java | 31 +++++++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 71a05283a..70ea1a4fb 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -62,10 +62,12 @@ public class ModListScreen extends Screen { private static final int TOGGLE_SHOW_LIBRARIES_BUTTON_ID = 5; private static final int MODS_FOLDER_BUTTON_ID = 6; private static final int DONE_BUTTON_ID = 7; + + I18n i18n = I18n.getInstance(); public ModListScreen(Screen previousGui) { this.parent = previousGui; - this.textTitle = I18n.getInstance().translateKey("modmenu.title"); + this.textTitle = i18n.translateKey("modmenu.title"); } public void updateEvents() { @@ -92,7 +94,6 @@ public void tick() { @Override public void init() { - I18n i18n = I18n.getInstance(); Keyboard.enableRepeatEvents(true); FontRenderer font = this.fontRenderer; paneY = 48; @@ -185,8 +186,8 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { this.buttons.add(configureButton); this.buttons.add(websiteButton); this.buttons.add(issuesButton); - this.buttons.add(ButtonUtil.createButton(MODS_FOLDER_BUTTON_ID, this.width / 2 - 154, this.height - 28, 150, 20, I18n.getInstance().translateKey("modmenu.modsFolder"))); - this.buttons.add(ButtonUtil.createButton(DONE_BUTTON_ID, this.width / 2 + 4, this.height - 28, 150, 20, I18n.getInstance().translateKey("modmenu.done"))); + this.buttons.add(ButtonUtil.createButton(MODS_FOLDER_BUTTON_ID, this.width / 2 - 154, this.height - 28, 150, 20, i18n.translateKey("modmenu.modsFolder"))); + this.buttons.add(ButtonUtil.createButton(DONE_BUTTON_ID, this.width / 2 + 4, this.height - 28, 150, 20, i18n.translateKey("modmenu.done"))); this.searchBox.setFocused(true); init = true; @@ -278,8 +279,6 @@ public void mouseReleased(int mouseX, int mouseY, int mouseButton) { @Override public void render(int mouseX, int mouseY, float delta) { - I18n i18n = I18n.getInstance(); - int dWheel = Mouse.getDWheel(); if (dWheel != 0) { double scrollDelta = dWheel > 0 ? 1.0 : -1.0; diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index b7942b1e7..d9dd0f396 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -24,8 +24,8 @@ public static List search(ModListScreen screen, String query, List return candidates; } return candidates.stream() - .filter(modContainer -> passesFilters(screen, modContainer, query.toLowerCase(Locale.ROOT))) - .collect(Collectors.toList()); + .filter(modContainer -> passesFilters(screen, modContainer, query.toLowerCase(Locale.ROOT))) + .collect(Collectors.toList()); } private static boolean passesFilters(ModListScreen screen, ModContainer container, String query) { @@ -34,12 +34,18 @@ private static boolean passesFilters(ModListScreen screen, ModContainer containe //Some basic search, could do with something more advanced but this will do for now I18n i18n = I18n.getInstance(); - String translationKey = "modmenu.nameTranslation." + modId; - String translatedName = I18n.getInstance().translateKey(translationKey); - String modName = !translatedName.equals(translationKey) ? translatedName : metadata.getName(); - + + String nameTranslation = "modmenu.nameTranslation." + modId; + String translatedName = i18n.translateKey(nameTranslation); + String modName = !translatedName.equals(nameTranslation) ? translatedName : metadata.getName(); + + String descriptionTranslation = "modmenu.descriptionTranslation." + modId; + String translatedDescription = i18n.translateKey(descriptionTranslation); + String modDescription = !translatedDescription.equals(descriptionTranslation) ? translatedDescription : metadata.getDescription(); + if (modName.toLowerCase(Locale.ROOT).contains(query) // Search mod name - || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod name + || modDescription.toLowerCase(Locale.ROOT).contains(query) // Search mod description + || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod id || authorMatches(container, query) // Search via author || (ModMenu.LIBRARY_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.library").toLowerCase(Locale.ROOT).contains(query)) // Search for lib mods || (ModMenu.CLIENTSIDE_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.clientside").toLowerCase(Locale.ROOT).contains(query)) // Search for clientside mods @@ -63,11 +69,10 @@ private static boolean passesFilters(ModListScreen screen, ModContainer containe private static boolean authorMatches(ModContainer modContainer, String query) { return modContainer.getMetadata().getAuthors().stream() - .filter(Objects::nonNull) - .map(Person::getName) - .filter(Objects::nonNull) - .map(s -> s.toLowerCase(Locale.ROOT)) - .anyMatch(s -> s.contains(query.toLowerCase(Locale.ROOT))); + .filter(Objects::nonNull) + .map(Person::getName) + .filter(Objects::nonNull) + .map(s -> s.toLowerCase(Locale.ROOT)) + .anyMatch(s -> s.contains(query.toLowerCase(Locale.ROOT))); } - } From 8dee600f215b4b1bf55aa315f06cb3582567a7c6 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:38:47 +0300 Subject: [PATCH 30/59] remove unnecessary @SuppressWarnings --- .../java/io/github/prospector/modmenu/gui/EntryListWidget.java | 3 +-- .../io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java | 1 - .../io/github/prospector/modmenu/util/TestModContainer.java | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java index 828c1c164..09fc45c0f 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Objects; -@SuppressWarnings({"unchecked", "unused"}) +@SuppressWarnings({"unused"}) @Environment(EnvType.CLIENT) public abstract class EntryListWidget> extends Screen { protected static final int DRAG_OUTSIDE = -2; @@ -575,6 +575,5 @@ public boolean charTyped(char c, int i) { public boolean changeFocus(boolean bl) { return false; } - } } diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java index 50678bb64..89672d307 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java @@ -15,7 +15,6 @@ @Mixin(value = ScreenPause.class, remap = false) public class MixinGuiIngameMenu extends Screen { - @SuppressWarnings("unchecked") @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { I18n i18n = I18n.getInstance(); diff --git a/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java b/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java index 8657b19a8..a67f03748 100644 --- a/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java +++ b/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java @@ -214,7 +214,6 @@ public Map getCustomValues() { return new HashMap<>(); } - @SuppressWarnings("UnstableApiUsage") @Override public boolean containsCustomElement(String key) { return false; From 95c351ee79c7a5a5ebee865134d0a2723fd41220 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Thu, 8 Oct 2026 22:25:46 +0300 Subject: [PATCH 31/59] Update ModMenuTexturedButtonWidget.java --- .../prospector/modmenu/gui/ModMenuTexturedButtonWidget.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java index 244d9f575..9f164fa39 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java @@ -7,6 +7,7 @@ import net.minecraft.client.render.renderer.GLRenderer; import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.client.render.window.CursorShape; public class ModMenuTexturedButtonWidget extends ButtonElement { private final String texture; @@ -51,6 +52,10 @@ public void render(Minecraft mc, int mouseX, int mouseY) { FontRenderer font = mc.font; boolean hovered = isHovered(mouseX, mouseY); + if (hovered && mc.currentScreen != null && this.enabled) { + mc.currentScreen.setDesiredCursor(CursorShape.HAND); + } + int adjustedV = this.v; if (!enabled) { adjustedV += this.height * 2; From a8324453ed313f0a4fbd823339f949696a0ac39e Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:28:48 +0300 Subject: [PATCH 32/59] Update ModListScreen.java --- .../java/io/github/prospector/modmenu/gui/ModListScreen.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 70ea1a4fb..44bec757a 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -113,6 +113,9 @@ public void init() { @Override public void render(Minecraft mc, int mouseX, int mouseY) { + if (isHovered(mouseX, mouseY)) { + setTooltip(i18n.translateKey("modmenu.configure")); + } if (selected != null) { String modid = selected.getMetadata().getId(); enabled = ModMenu.hasConfigScreenFactory(modid) || ModMenu.hasLegacyConfigScreenTask(modid); From b235e3b1489130030b1f73d51763a95ba5862dd4 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:41:52 +0300 Subject: [PATCH 33/59] Update ModListScreen.java --- .../java/io/github/prospector/modmenu/gui/ModListScreen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 44bec757a..9b9e9ae8b 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -190,7 +190,7 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { this.buttons.add(websiteButton); this.buttons.add(issuesButton); this.buttons.add(ButtonUtil.createButton(MODS_FOLDER_BUTTON_ID, this.width / 2 - 154, this.height - 28, 150, 20, i18n.translateKey("modmenu.modsFolder"))); - this.buttons.add(ButtonUtil.createButton(DONE_BUTTON_ID, this.width / 2 + 4, this.height - 28, 150, 20, i18n.translateKey("modmenu.done"))); + this.buttons.add(ButtonUtil.createButton(DONE_BUTTON_ID, this.width / 2 + 4, this.height - 28, 150, 20, i18n.translateKey("gui.options.button.done"))); this.searchBox.setFocused(true); init = true; From b0c2ab660ba15a943e566ef69b1e8df6566413cf Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:42:32 +0300 Subject: [PATCH 34/59] Update en_US.lang --- src/main/resources/assets/modmenu/lang/en_US/en_US.lang | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index 774002d20..3658137ad 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -2,7 +2,6 @@ modmenu.title=Mods modmenu.loaded=(%s Loaded) modmenu.config=Edit Config modmenu.modsFolder=Open Mods Folder -modmenu.done=Done modmenu.configsFolder=Open Configs Folder modmenu.configure=Configure... modmenu.modIdToolTip=Mod ID: %s @@ -11,7 +10,7 @@ modmenu.issues=Issues modmenu.authorPrefix=By %s modmenu.authors=Authors: modmenu.contributors=Contributors: -modmenu.licenses=Licenses: +modmenu.licenses=License: modmenu.search=Search for mods modmenu.toggleFilterOptions=Toggle Filter Options modmenu.showLibraries=Libraries: %s From 98519984772d862665618124924f8bee7fe304f5 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:10:26 +0300 Subject: [PATCH 35/59] Update DescriptionListWidget.java --- .../modmenu/gui/DescriptionListWidget.java | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 12e2065d0..21ea3b265 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -113,22 +113,6 @@ public void render(int mouseX, int mouseY, float delta) { Collection contributors = selectedEntry.getMetadata().getContributors(); Collection licenses = selectedEntry.getMetadata().getLicense(); - if (!authors.isEmpty()) { - if (!children().isEmpty()) children().add(new DescriptionEntry("")); - children().add(new DescriptionEntry(i18n.translateKey("modmenu.authors"))); - for (Person person : authors) { - children().add(new DescriptionEntry(" " + person.getName())); - } - } - - if (!contributors.isEmpty()) { - if (!children().isEmpty()) children().add(new DescriptionEntry("")); - children().add(new DescriptionEntry(i18n.translateKey("modmenu.contributors"))); - for (Person person : contributors) { - children().add(new DescriptionEntry(" " + person.getName())); - } - } - ContactInformation contact = selectedEntry.getMetadata().getContact(); Map links = new HashMap<>(contact.asMap()); @@ -158,13 +142,33 @@ public void render(int mouseX, int mouseY, float delta) { }); } - if (!licenses.isEmpty()) { - if (!children().isEmpty()) children().add(new DescriptionEntry("")); - children().add(new DescriptionEntry(i18n.translateKey("modmenu.licenses"))); - for (String license : licenses) { - children().add(new DescriptionEntry(" " + license)); - } - } + if (!licenses.isEmpty()) { + if (!children().isEmpty()) children().add(new DescriptionEntry("")); + children().add(new DescriptionEntry(i18n.translateKey("modmenu.licenses"))); + for (String license : licenses) { + children().add(new DescriptionEntry(" " + license)); + } + } + + if (!authors.isEmpty() || !contributors.isEmpty()) { + if (!children().isEmpty()) children().add(new DescriptionEntry("")); + + children().add(new DescriptionEntry(i18n.translateKey("modmenu.credits"))); + + if (!authors.isEmpty()) { + children().add(new DescriptionEntry(" " + i18n.translateKey("modmenu.authors"))); + for (Person person : authors) { + children().add(new DescriptionEntry(" " + person.getName())); + } + } + + if (!contributors.isEmpty()) { + children().add(new DescriptionEntry(" " + i18n.translateKey("modmenu.contributors"))); + for (Person person : contributors) { + children().add(new DescriptionEntry(" " + person.getName())); + } + } + } } super.render(mouseX, mouseY, delta); } From 5192a045723c1d32d3447b2e359566b6c8988af0 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:11:53 +0300 Subject: [PATCH 36/59] Update en_US.lang --- src/main/resources/assets/modmenu/lang/en_US/en_US.lang | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index 3658137ad..2859632c6 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -8,8 +8,6 @@ modmenu.modIdToolTip=Mod ID: %s modmenu.website=Website modmenu.issues=Issues modmenu.authorPrefix=By %s -modmenu.authors=Authors: -modmenu.contributors=Contributors: modmenu.licenses=License: modmenu.search=Search for mods modmenu.toggleFilterOptions=Toggle Filter Options @@ -21,6 +19,10 @@ modmenu.sorting=Sort: %s modmenu.sorting.ascending=A-Z modmenu.sorting.descending=Z-A +modmenu.credits=Credits: +modmenu.authors=Authors: +modmenu.contributors=Contributors: + modmenu.searchTerms.library=api library modmenu.searchTerms.clientside=clientside gameside modmenu.searchTerms.deprecated=deprecated outdated old From 19224456494501e5af8ad467593c847c0ffb58d6 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:44:20 +0300 Subject: [PATCH 37/59] Add view credits, links for bta --- .../modmenu/gui/DescriptionListWidget.java | 43 +++++++++++++++++-- .../prospector/modmenu/gui/ModListScreen.java | 30 ++++++++++--- .../assets/modmenu/lang/en_US/en_US.lang | 3 +- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 21ea3b265..565cbfa95 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -5,6 +5,7 @@ import net.fabricmc.loader.api.metadata.ContactInformation; import net.fabricmc.loader.api.metadata.Person; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScreenCredits; import net.minecraft.client.render.font.FontRenderer; import net.minecraft.client.util.helper.UrlHelper; import net.minecraft.core.lang.I18n; @@ -71,6 +72,35 @@ public void mouseClicked(int mouseX, int mouseY, int button) { } } + protected class BTACreditsEntry extends DescriptionEntry { + private final int indent; + private int lastRenderX; + private int lastRenderY; + + public BTACreditsEntry(String text, int indent) { + super(text); + this.indent = indent; + } + + @Override + public void render(int index, int y, int x, int itemWidth, int itemHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + this.lastRenderX = x + indent; + this.lastRenderY = y; + + String formattedText = TextFormatting.formatted(text, TextFormatting.BLUE, TextFormatting.UNDERLINE); + this.drawStringShadow(this.fontRenderer, formattedText, lastRenderX, lastRenderY, 0xFFFFFF); + } + + public void mouseClicked(int mouseX, int mouseY, int button) { + if (button == 0) { + int textWidth = fontRenderer.stringWidth(text); + if (mouseX >= lastRenderX && mouseX <= lastRenderX + textWidth && mouseY >= lastRenderY && mouseY <= lastRenderY + 12) { + mc.displayScreen(new ScreenCredits(mc.currentScreen)); + } + } + } + } + @Override public void render(int mouseX, int mouseY, float delta) { I18n i18n = I18n.getInstance(); @@ -144,12 +174,20 @@ public void render(int mouseX, int mouseY, float delta) { if (!licenses.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); - children().add(new DescriptionEntry(i18n.translateKey("modmenu.licenses"))); + children().add(new DescriptionEntry(i18n.translateKey("modmenu.license"))); for (String license : licenses) { children().add(new DescriptionEntry(" " + license)); } } + if ("minecraft".equals(id)) { + if (!children().isEmpty()) children().add(new DescriptionEntry("")); + String viewCreditsText = i18n.translateKey("modmenu.viewCredits"); + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, viewCreditsText, getRowWidth() - 16)) { + children().add(new BTACreditsEntry(line, 0)); + } + } + if (!authors.isEmpty() || !contributors.isEmpty()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); @@ -177,7 +215,7 @@ public void render(int mouseX, int mouseY, float delta) { public void mouseClicked(int mouseX, int mouseY, int button) { super.mouseClicked(mouseX, mouseY, button); for (DescriptionEntry entry : children()) { - if (entry instanceof LinkEntry) { + if (entry instanceof LinkEntry || entry instanceof BTACreditsEntry) { entry.mouseClicked(mouseX, mouseY, button); } } @@ -201,5 +239,4 @@ public void render(int index, int y, int x, int itemWidth, int itemHeight, int m this.drawStringShadow(this.fontRenderer, text, x, y, 0xAAAAAA); } } - } diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 9b9e9ae8b..4da4151ce 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -62,7 +62,7 @@ public class ModListScreen extends Screen { private static final int TOGGLE_SHOW_LIBRARIES_BUTTON_ID = 5; private static final int MODS_FOLDER_BUTTON_ID = 6; private static final int DONE_BUTTON_ID = 7; - + I18n i18n = I18n.getInstance(); public ModListScreen(Screen previousGui) { @@ -135,16 +135,16 @@ public void render(Minecraft mc, int mouseX, int mouseY) { ButtonElement websiteButton = new ButtonElement(WEBSITE_BUTTON_ID, rightPaneX + (urlButtonWidths / 2) - (cappedButtonWidth / 2), paneY + 36, Math.min(urlButtonWidths, 200), 20, i18n.translateKey("modmenu.website")) { @Override public void drawButton(Minecraft mc, int var1, int var2) { - visible = selected != null; // visible = selected != null - enabled = visible && selected.getMetadata().getContact().get("homepage").isPresent(); + visible = selected != null; + enabled = visible && ("minecraft".equals(selected.getMetadata().getId()) || selected.getMetadata().getContact().get("homepage").isPresent()); super.drawButton(mc, var1, var2); } }; ButtonElement issuesButton = new ButtonElement(ISSUES_BUTTON_ID, rightPaneX + urlButtonWidths + 4 + (urlButtonWidths / 2) - (cappedButtonWidth / 2), paneY + 36, Math.min(urlButtonWidths, 200), 20, i18n.translateKey("modmenu.issues")) { @Override public void drawButton(Minecraft mc, int var1, int var2) { - visible = selected != null; // visible = selected != null - enabled = visible && selected.getMetadata().getContact().get("issues").isPresent(); + visible = selected != null; + enabled = visible && ("minecraft".equals(selected.getMetadata().getId()) || selected.getMetadata().getContact().get("issues").isPresent()); super.drawButton(mc, var1, var2); } }; @@ -210,11 +210,27 @@ protected void buttonClicked(ButtonElement button) { break; } case WEBSITE_BUTTON_ID: { - Objects.requireNonNull(selected).getMetadata().getContact().get("homepage").ifPresent(UrlHelper::openURL); + if (selected != null) { + String modId = selected.getMetadata().getId(); + + if ("minecraft".equals(modId)) { + UrlHelper.openURL("https://www.betterthanadventure.net/"); + } else { + selected.getMetadata().getContact().get("homepage").ifPresent(UrlHelper::openURL); + } + } break; } case ISSUES_BUTTON_ID: { - Objects.requireNonNull(selected).getMetadata().getContact().get("issues").ifPresent(UrlHelper::openURL); + if (selected != null) { + String modId = selected.getMetadata().getId(); + + if ("minecraft".equals(modId)) { + UrlHelper.openURL("https://support.betterthanadventure.net/"); + } else { + selected.getMetadata().getContact().get("issues").ifPresent(UrlHelper::openURL); + } + } break; } case TOGGLE_FILTER_OPTIONS_BUTTON_ID: { diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index 2859632c6..dac142617 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -8,7 +8,7 @@ modmenu.modIdToolTip=Mod ID: %s modmenu.website=Website modmenu.issues=Issues modmenu.authorPrefix=By %s -modmenu.licenses=License: +modmenu.license=License: modmenu.search=Search for mods modmenu.toggleFilterOptions=Toggle Filter Options modmenu.showLibraries=Libraries: %s @@ -22,6 +22,7 @@ modmenu.sorting.descending=Z-A modmenu.credits=Credits: modmenu.authors=Authors: modmenu.contributors=Contributors: +modmenu.viewCredits=View Credits modmenu.searchTerms.library=api library modmenu.searchTerms.clientside=clientside gameside From 2c943146d8b1e48d3f721f4a4ab8fe0783f35adc Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:05:31 +0300 Subject: [PATCH 38/59] update buttons --- .../modmenu/textures/gui/configure_button.png | Bin 4658 -> 1932 bytes .../modmenu/textures/gui/filters_button.png | Bin 4575 -> 1688 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/modmenu/textures/gui/configure_button.png b/src/main/resources/assets/modmenu/textures/gui/configure_button.png index e84c234a44101cf7c36e7fecb0a020b9ccf9fcd1..49e8429c138bd1660b55d9657a0ec070d8d8897a 100644 GIT binary patch delta 1918 zcmV-^2Z8vqB#aM`BYy`|Nkln*g^dg2Li@6I(M=O;s@4E)N~us9q2VzzFnE6F z>-qTR0emylq?;b{&As1!ob$iu{O`G!FXEhAk>|e2%F0Tl5PxMc1`iJp=ge7NUe>l~ zG@8?A4UtI1i9vlmfBw8192~T+(4_)kGz#(w2GZ%YD=#m1sZ`441C&T4+~nkBzUdpb z`2aA)s$(&)si&t$C(q8#>dF9N##uSz07N@=>)X&`)&RAJ5|pya?(S}X$iagL-O-~* zT}MZU-xjP)On*$cp`jrKV4(>1hp8)ova&LNY+qlWL-FzB$JIC;U0q$eQdS926W%dg zS68Rtt-X*^0W2*o`D8+2Q&W?={mz{`YS`L>#xX)WVY9Lj!!}j{Slm&@s_+O;V`HNl zU@+lHCX@P3A7FTFt|=_EQ~*qZ2Pme*5C(%Q?(FPT-G7jBU|>MU(O_V-vxpxBfFWb? z;lqd5R0A1%dwU(^MBoZJ1i);?VnhKDLmpPs-{0?U-n^-+2NYY4J`D39M~)ov$J1Y5 zU+?1acro^!9|F99p~b~Tw{`1Q1;D#y#1kh@xasL>bv;J6Z{O~Fjc@_J81VrFC!-AG z78-bg(SMqn8lQ9k#Y3zd_b)6g1XYBt`v5SCA%ty-n-R-|upK}=jIFAw(tdtuD>(!x z#fY&T#qI6wj>NQHCWM4WVCEDmz6V$}0?|l7q+pCAoC_8bycEnA^fxy*s~4yjDhj|{ zZzUvzbW>APy2UCgDl{qKA;wtdgnWczety0*5`SL2cv1H>=*<8|O(5Wn8#nY7!V&LM z+-`a)b?=Z!OfU@F;RHg7`zawCE5F)4 zI*q#aw>`sH@98_~K4{8XI*Jb!XT%C4Rx_{sf*o58g`a;Cx4VYb8iI^Az#|HwmqqCzdboBhUG{W4zx&H zaNmQ%!Ujj|*!Izdtf&VhqpCnM$VRO3N^{HO3W&~lJSKOAB_(1kwt6Qt&UhLM|Mf`- znNZAX78Vk&veG+#P38VKp3*9${PLSI0k$k4IhwsA+Qt=HDgeo>V%%eY$GfW}%zswa z)qB!)cIMn@&zRExc9$= zqAKvm=Rj~C#_F&hj&8rSgECQVE69b zst311CbFU(y|f{;bL8?^IDfPw@N@a{<-l9Gssb=$#$;m7gIL{5)vKTCg6iqB@xk^m zbp>FP%>zt4+tA?8f8q9RHDJV~1F)y$>lpIvty?~6wV{BY91jEyzk2DC`(f|i3^3>h zQ1Av+F92p*TMIC|I#dCyzB>1!{AaxR`gJvoaReYisg;*(gBHf%xPLJ9j1X9yQO+uB z@BKrHqW@Z2d}8KPY+=mTw_aO@2XJd}7u?;kL(hK{#6s+X^$e}mm*D|O1`&;nLAM&= zQ1qO98D|V-oqSO%MHE1g-RHJ#TT`Er5~GAk2`gVyZI`S$ah;AF54=1xiU0rr literal 4658 zcmd5=2~-p379P|pP+2Tmz=aNRL(62F>=Ho%*$f~cD55f%nLq?GF$oZsY6`J#L9K!d zwJ27QJ`r1#R}=*miU=a~Sy6n5q9_UiE^I!xH({%NN2}+(zH^#$lKE%m{`b4fckh2s zHu`$InHWzr1^~ds!`;P?yn^J{$#69Jo3JH1kGvSk-Ipo>;A0E@*8oW0YegL@uW4FC>~YB_>N;3`mzhe>5ZN@r0C1(ae!N)TH_7s;LRaH)H| z0{4&i4nX50P(DU+oC`Xr1*C!~T!nz@s7RSopcYd4^$N(f{xOXL_KT<@gp_%DKyZo3 z7jz~RILM}QAe6ykf?Ph8!J+e+EPIeiXK-nBE{y?0bcTS=5x{(K@IxWd6qrQd=dxfB zi@Xz3!c{7{fJTdrjituIR6-F(WAOQW8l6dFG9gj|QpU+th#Hb9rw(aw!Ih{&DpyGf z8K~EYh>2*GkV0BI;6jvqSglMs7$!1cG&LfpF{pICOZ|cvIxHuTRz&uLV<-)e#G`PT zN=eExhGpg9go;px6Yo$x{Q7qQWN1aAVT`xh5*0NJp;WoXkZueHb%;{BWD0%rw|s0c*>K|~H^*>}ia(Aim^RUT*;B$Z)A zta6qfaBvUqf~asIg?!J4=q!lA2w<=T45k2PKy{vM>rboCbk25lJXWVA4AH&F3D@&b|slB8?W^CJNl8N;1uHgN5jiuO3`QO2K~e2@q6YR6+`>Cj^dB1|Lh`7LN}>9^4-rj+09N zfF%RyN{lW&e{4M}=CGL<8FdaU=0hBmT=97*0dlO~Y1n^J9dvBiTOa{ioBq+pW z!5oNyr`?SZI4zv%4qD^<0CZ(nwzns$*~WwWp1kZcFxS|>gxQM_TU>n zB2k(!p@&xk*xT7vtOeY*+C-a{x1ZlKzDA5vJmr~)=IG09(bWwyt{ zg{jc8zh7Gq0AV#5X(vw#3a$b7+uPGV56W{-OgxpRR4B%WOQql22OoDoZ8>pbtD|P; z&QFD9g*KZP1utGa{c7Iw9YIG|T&UVb%Q|}0$8?I|U_wetN`8FzDw9bo>Q6=={=#(k zw9+TJx%P|u`W~ZkPw%CiTJtjaXnT9R--;E~&6c#DO3MhqZp^4U^UE0-HgS({uRC5) zaN*05W2Vt)KC*_wOz@HS!iBo0QzC$0;q`mxei3YH+a=sS$p8p z_iNKln)dSzvJW5js;sPJJwGwV-1Cb1V%lUU-uSAPnth!QGtD>ZbgIhnW`L^kRGh#v zDLJ`4%wW9|-1$xUcuUKfMX{Yb4<4NM(YgYOO|5RH{n7&ofk&)*@66X`=1VWn2ovEK z_rPwF>h!F9*$u%BxL5cy0c7u*iXg}*$fjTKCvqyl!u zyHa0Gk9WQP`qO7m^DEX>H9r5$%kAd+niE;=F#zw(uIAK`?YRdwZ(rG%8MLzI;N9b% z?p?GPz&G{BlIpfw4{v+rm*ih=(l#|Uo!l2xM|E|Keh}vL#md?lO{+~_jSO!a*AiN4 zv=n6rl^P*Vno9dfCC@f(TSn}>bKP6rh)842cT!16K`=v_$MoLkVK zzGUpw;@GL!@8}(#`*5oTb6XN6;^i2pJ7>4Q;$<4tel|1Ygd*z;LtmgiG>1NA%x&$d zi_I-9ZC0b5T+R_4(eqo^g5HLX`$wirun8l8-Py^B)R|q8SLrQeP@EadC{!_X%}|PtU4~e zjQy+Y0TV4BJE5+E=4nTXF;(koRmMyKikeOuQ;PAV}fR@Fm%*9b!Kt#irKSg zV-1C?_o=mEGeqeJgMu7ZQRiv+608}n}oU`b=$v@QD(!n3BvaGGGJuh_LD4H4D(eO1KcIm#M zWUKX;-5H3oz_`x@SV#7XwDo46wFWdBn_Vl4J*u-2UOzpXzM5_A@a^2Dy*DQ>4T*|6(tPjU-_q<#*1S9!9MQ4E63}ch(3R;@Qqqvp zuV$IInOgxbu;eO(v)2KzW5)Pe<{J1MJavJtc2q&nnX-)*_{H6f^q12no-*(8TD-d~ zvFrZV%qM%?~H)^u>Ou%@WKRG2Y@6MRG#f+H+c?6`~6SV8;@T=`Z${ zDNSSpF&<%dOQDIoH##&2EHT)9HhKE5{BECPwv%6&n^-k|6=%?7{nT-U=ap`xEOUu> zNVSdeIC$Fzt_^ViQB&!+HewS^Dc`(G)dd HADZ-U{IKma diff --git a/src/main/resources/assets/modmenu/textures/gui/filters_button.png b/src/main/resources/assets/modmenu/textures/gui/filters_button.png index 9d8be05b1a1ef76128bfdf6593e40c1a7a16d808..de8495a3fed72fb9718aeab904abc573a8fe3e2f 100644 GIT binary patch delta 1673 zcmV;426p-1BbW`4BYy^8NklO8oa_1Lf0JIXSIT2-3gwNBjiEK2PRGV7m4Ax0-EP;F%jF?G4Qy>~ zxrYxQ>UHea2%wFDEb$&Wz#&p9l^pN1$n^BId-LXvJ3T#h2L}gP%SVm{0vN{Ysi`nz zz`P3(9ijjsq}6H}G5U%W{XAl57z6~5*4EbYtapoxi$)xBMZ^fixm{jfcB`waW-CdO zOq=z3ePDiJVSmBMXDW{vw5lS9c)&w6mX?-m89>*rU2|8iTygXB^Y&zcvra`j0&i&A z2q^C|LPS2T1n`6hB7ZY8Ge!nNwOTD~L0P|k{kpmq zEg~k}Fp;AZHa*~wAp8h@ic@tF+mtR*qE8JeVzJ&h{ZIq3vW#IoeZWu~iWY0=6Mjy_ zA`%D)6eD;J5CqafLAOhD;xN+k~s14?+7u7&kv}EtB)(Bt>QA?lP@=ksUFZ({d)c_?DaimzA zM~uMs_O{#K-?w#uVz64R+B-RymodRSAZO`X5ps}70G^$l4dEvZiiUKQ8s629Qz*T0 zTFQL#^E5*8WQVs-G@tHkDqcBJAa?+J}fPpg3wevMc%1XpN%15j9S*=#w>(@=cJ38*=%fB+$j~Wa<0n}>s zZ^ByC?e_dOs~9+AcnT3(=1xvd{oz$HBIzSrHb%7;0m6$%wOYm9`z{%^W?=rO-yZvP zb$>l65|&?#K%CoO{@8SPzP)Ldk|fErn7)?X{OYb9NSVqb291C}{@;2~GH>qwUUxr# zpE!W%lbTA!C*I40A3wKNGd!QbX?3;GceR z$H18@Dq&@()Aa?#N2q6&jn{aOP!|wEE*< zX>i`9*Jw<5CuA2;#eiNoKW$`)#(CdQJNV_-_k+VkOMel^R4WTHn=yR~}kbmR! z0n=`C4KMkv=`W)#e-io~fZjwY02HH~?ae0lzJE24ru}OlaFWIiT@5vA|V2J z#)Bvt0@qHs{mriyex-qWAx(TQif%_Ez%D?@Z9nAwTM(X--wPM2j(tx*7Qkz@skbL( z|EHXIew@A$`19$ed-39h>2vSv_Ia^DZx!F+Bs_HoOF@Vy7p3<97)i(kH)3plY{oT<2M$i|C ztn#k^CFB1qG(T=@l_tRSr9u&j!9|ue=rad#kslXWdP5P|-QCsPSS{&Ow1380kWw-N z@fg4P?dP?99^ROrWd!poO0yTil7FqgN(t)r(RRCqC>NL;;oH5^8aKYR9!#)%x@ zioo;d&*@`xY#I0*s|FxKBoK&@UvixW=Lc%QnTzXWY;><^DUb&kq3~3Ecn<+Z1H;MN zv>Y8B8F(DEJiyYm2*?AB(SOHd8HR);24_saj&XD1DI!A=z$oWB1R#<&{OZ-KfTKgI zX!tQSunkE?3`k!f0o8XPr`$7M^bBC_&^BaH0QA$|>GK@x#H3l1M}{K6VnFbabf`dO zl!ypCJ_$9Sw;dpWw=#;MxUT{v+>n4AzbhBL2Z*Tc0u1s1h~Ocj@PBJ393LNBz9FG{ zQ7uT<7UuXfPy>)aFot1)@LM4JK1&plthsTv1A+G$fqwuc(twO~ T7Y}v-0000c2%UV7nMV+t3_G`tZvYP+G0Vftw(jM#6;+WhNhGb=vf}n}n6cCWc*=Q&`E!AYDvbC~qxfEDCw-qv|o5Yr) zm4!F~p&5o4NGDo22$!p22EkMiLCO)el2l<6Ae9m!6iPyYps*65lxhkkq27-SpjlX+ znywG;#R7L)S+dP$rWA^-tSosJDiTXRe{IvX}w1N$WCIV)ok z%r-$ZK~9OZQOvMuWkAy&6Vl8su_kM;n}EX<*|b@K$dyi$x(QjvMQ6^iq;`X|jDkz$ z(m0dN3TP1*tvOk=iPmKCKB8TZ{{sNH)?jd945&+5nhU~e3(Ev%^g3ifYHM7MnNv*X ztYU_R;leV3nt^@RSZ&j}-~IUr?0~$R*-Ysy9BmUVaiW;o7v-2fdm)|98C4DRV!U8t z#VqSfPQYFsPEXr7tqeRTVI>A5NF0Juh>AiHSV>SyWe=%AWCcE_KPdsL)Nv}5QmQBn zWJ*6$ka8?-qaRAlG88Xb(rDl?A&pMr6lPPB4045m(ut{}1pou>(Ej_8I$exKa{Xpt6UIRiX}CuiX%A~!;L)5vzQX5 zX^erDI7u4`LPKgaL?6FiWHOv7wFx0;@S(1#<|7!y1AiVH77Y4ay@*0wtYg z^E+~%ln`K_6$IVdTb7}8Qtv8N2pU*vhB0zV1ZQD}0P#j~jD<-Q)c|LxXr4z9wT3_^ zDx4JwL_j^O9c(19g`MMKjKl*UA`Yx^k?ncUu@BT94$H zUjD3P4hVff4)>s2Mc$T0Tey%U5ZOm56UF`Xx6+vp)}Aq{aTN=4LX8?pSj~VHsbOds z=Zz|cBMA@(tk3#^v{xY%j`fw*|EPVhbC_h>l*EB4qLAI!9!4}}a+ZD(G1C?r3=hs? z)yjB_muA7Z#AEGu-P5dR6e<3DO7-09dzw z6&DNNl1U^Umm>8camnA^`1AFsv6Fq4+=&c+>&Uo(o1-G2pLPu!?4C_8Ytkan#WEoc(J*->I{&?%2LPue!0RltSOp z%L39bw#~UXt0sEi{{27is;Uxv_HAo9bH=k`&zZ7k-MpoTo0^KIO`8@WUxzX+#Wo5q zFE5|(AuXzB3v?$=oEYBN*x2qK6&-!t;V7#r-mn4tf}*I+el4x7L%qMLDXFYH;q%M* z4aLRB?Dl0V-hDSRFR%KLhYxi6+_`h>+;88y6%il5?p8;~6xD}bC%^w*j98=X(7&*2 z_ioxdJ+|S3M?p->fn!U(Hg4Q_4=)J|e~RaMFZ<9B)~`oX9G-!f48bF=2q&9{8~wLF zRctuAT+-Dr*U3K%-d{TLcy-t z@eTNCp8yC>TK{Bm@JC0(^JPsJWA_9!^OyHMi^P z3ZDMF?1M!)F9$E#zjv>PRcc=4i{bc(uQjLXUnrUF9f5Zm5|Dr3Q-i~owdf&+fX}@vlbz!ucK=$ZP+zH*mlaca z{v6wOv}M&PPscNJBF=Biy?ZIcPZ~;Je|d~*OL+6$6bVfJ@^j_NAMO$U&?|{otLHR~ z&a1O$`~7mO_DJpWsYAkJiDk2j91Eq<6~BEZ9ozX*&ceF_Oq=_t z5z(QQlji3d_inHGWdH0bxw)`HdCsjxw{XpH(eo`s0CR`*2nek?cjcrK?+8-z?p^6HWBKcKv#T{h5wy^8o+iaoeC7 zufM)F;K-PetC25uJ0&!na)6e06EbQ!Gti*m^Pcj4`mJ z_MBv{rp6QkpR{{OlZQN?e|>Rg?GoST0#|z`AHVrr=BcWsBd;v(sy}Z3TjJh$|G0Rz z$^G>aH(x0E*W3i(EhQy2E9QP3d?z4`VVJw(;jXQIa|b&%FK_Uyy#2moR+kSE!NSd_jZG_l~{@ Date: Fri, 9 Oct 2026 19:13:25 +0300 Subject: [PATCH 39/59] add config screen --- .../modmenu/config/ModMenuConfig.java | 78 ++++++++++- .../config/ModMenuConfigIntegration.java | 18 +++ .../modmenu/config/ModMenuConfigScreen.java | 122 ++++++++++++++++++ .../gui/AlwaysSelectedEntryListWidget.java | 5 - .../prospector/modmenu/gui/ModListScreen.java | 10 +- .../modmenu/mixin/MixinGuiIngameMenu.java | 24 +++- .../modmenu/mixin/MixinGuiMainMenu.java | 27 +++- .../assets/modmenu/lang/en_US/en_US.lang | 20 ++- src/main/resources/fabric.mod.json | 3 + 9 files changed, 274 insertions(+), 33 deletions(-) create mode 100644 src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java create mode 100644 src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index c0d4351b2..03daf131a 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.config; - import net.fabricmc.loader.api.ModContainer; import net.minecraft.core.lang.I18n; @@ -9,6 +8,8 @@ public class ModMenuConfig { private boolean showLibraries = false; private Sorting sorting = Sorting.ASCENDING; + private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; + private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; public void toggleShowLibraries() { this.showLibraries = !this.showLibraries; @@ -20,6 +21,16 @@ public void toggleSortMode() { ModMenuConfigManager.save(); } + public void toggleGameMenuButtonStyle() { + this.gameMenuButtonStyle = GameMenuButtonStyle.values()[(gameMenuButtonStyle.ordinal() + 1) % GameMenuButtonStyle.values().length]; + ModMenuConfigManager.save(); + } + + public void toggleModsButtonStyle() { + this.modsButtonStyle = ModsButtonStyle.values()[(modsButtonStyle.ordinal() + 1) % ModsButtonStyle.values().length]; + ModMenuConfigManager.save(); + } + public boolean showLibraries() { return showLibraries; } @@ -28,9 +39,38 @@ public Sorting getSorting() { return sorting; } + public GameMenuButtonStyle getGameMenuButtonStyle() { + return gameMenuButtonStyle; + } + + public ModsButtonStyle getModsButtonStyle() { + return modsButtonStyle; + } + + public String getSortingDisplayString() { + I18n i18n = I18n.getInstance(); + return i18n.translateKey("option.modmenu.sorting") + ": " + this.sorting.getName(); + } + + public String getShowLibrariesDisplayString() { + I18n i18n = I18n.getInstance(); + String librariesValueKey = this.showLibraries ? "option.modmenu.show_libraries.true" : "option.modmenu.show_libraries.false"; + return i18n.translateKey("option.modmenu.show_libraries") + ": " + i18n.translateKey(librariesValueKey); + } + + public String getModsButtonStyleDisplayString() { + I18n i18n = I18n.getInstance(); + return i18n.translateKey("option.modmenu.mods_button_style") + ": " + this.modsButtonStyle.getName(); + } + + public String getGameMenuButtonStyleDisplayString() { + I18n i18n = I18n.getInstance(); + return i18n.translateKey("option.modmenu.game_menu_button_style") + ": " + this.gameMenuButtonStyle.getName(); + } + public enum Sorting { - ASCENDING(Comparator.comparing(modContainer -> modContainer.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER),"modmenu.sorting.ascending"), - DESCENDING(ASCENDING.getComparator().reversed(),"modmenu.sorting.descending"); + ASCENDING(Comparator.comparing(modContainer -> modContainer.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER), "option.modmenu.sorting.ascending"), + DESCENDING(ASCENDING.getComparator().reversed(), "option.modmenu.sorting.descending"); final Comparator comparator; final String key; @@ -48,4 +88,34 @@ public String getName() { return I18n.getInstance().translateKey(key); } } -} + + public enum ModsButtonStyle { + INSERT("option.modmenu.mods_button_style.insert"), + ICON("option.modmenu.mods_button_style.icon"); + + final String key; + + ModsButtonStyle(String key) { + this.key = key; + } + + public String getName() { + return I18n.getInstance().translateKey(key); + } + } + + public enum GameMenuButtonStyle { + INSERT("option.modmenu.game_menu_button_style.insert"), + ICON("option.modmenu.game_menu_button_style.icon"); + + final String key; + + GameMenuButtonStyle(String key) { + this.key = key; + } + + public String getName() { + return I18n.getInstance().translateKey(key); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java new file mode 100644 index 000000000..19019d8f1 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java @@ -0,0 +1,18 @@ +package io.github.prospector.modmenu.config; + +import io.github.prospector.modmenu.api.ModMenuApi; +import net.minecraft.client.gui.Screen; + +import java.util.function.Function; + +public class ModMenuConfigIntegration implements ModMenuApi { + @Override + public String getModId() { + return "modmenu"; + } + + @Override + public Function getConfigScreenFactory() { + return ModMenuConfigScreen::new; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java new file mode 100644 index 000000000..6492b4b90 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -0,0 +1,122 @@ +package io.github.prospector.modmenu.config; + +import io.github.prospector.modmenu.gui.ModListScreen; +import net.minecraft.client.gui.ButtonElement; +import net.minecraft.client.gui.Screen; +import net.minecraft.core.lang.I18n; +import org.lwjgl.input.Keyboard; + +public class ModMenuConfigScreen extends Screen { + private final Screen parent; + + public ModMenuConfigScreen(Screen parent) { + super(parent); + this.parent = parent; + } + + @Override + public void init() { + I18n i18n = I18n.getInstance(); + ModMenuConfig config = ModMenuConfigManager.getConfig(); + + int buttonWidth = 150; + int buttonHeight = 20; + int spacing = 10; + int startY = this.height / 6; + + int totalWidth = (buttonWidth * 2) + spacing; + int leftX = this.width / 2 - totalWidth / 2; + int rightX = leftX + buttonWidth + spacing; + + this.add(new ButtonElement( + 100, + leftX, + startY, + buttonWidth, + buttonHeight, + config.getSortingDisplayString() + )); + + this.add(new ButtonElement( + 101, + rightX, + startY, + buttonWidth, + buttonHeight, + config.getShowLibrariesDisplayString() + )); + + this.add(new ButtonElement( + 102, + leftX, + startY + 24, + buttonWidth, + buttonHeight, + config.getModsButtonStyleDisplayString() + )); + + this.add(new ButtonElement( + 103, + rightX, + startY + 24, + buttonWidth, + buttonHeight, + config.getGameMenuButtonStyleDisplayString() + )); + + int doneButtonWidth = 200; + this.add(new ButtonElement( + 200, + this.width / 2 - doneButtonWidth / 2, + this.height - 28, + doneButtonWidth, + buttonHeight, + i18n.translateKey("gui.options.button.done") + )); + } + + @Override + public void render(int mouseX, int mouseY, float delta) { + this.renderBackground(); + + I18n i18n = I18n.getInstance(); + this.drawStringCenteredNoShadow(this.fontRenderer, i18n.translateKey("modmenu.options"), this.width / 2, 20, 0xFFFFFF); + + super.render(mouseX, mouseY, delta); + } + + @Override + protected void buttonClicked(ButtonElement button) { + ModMenuConfig config = ModMenuConfigManager.getConfig(); + + if (button.id == 100) { + config.toggleSortMode(); + button.displayString = config.getSortingDisplayString(); + } else if (button.id == 101) { + config.toggleShowLibraries(); + button.displayString = config.getShowLibrariesDisplayString(); + } else if (button.id == 102) { + config.toggleModsButtonStyle(); + button.displayString = config.getModsButtonStyleDisplayString(); + } else if (button.id == 103) { + config.toggleGameMenuButtonStyle(); + button.displayString = config.getGameMenuButtonStyleDisplayString(); + } else if (button.id == 200) { + this.closeAndReturn(); + } + } + + @Override + public void keyPressed(char eventCharacter, int eventKey, int mx, int my) { + if (eventKey == Keyboard.KEY_ESCAPE || eventKey == Keyboard.KEY_BACK) { + this.closeAndReturn(); + } + } + + private void closeAndReturn() { + if (this.parent instanceof ModListScreen) { + ((ModListScreen) this.parent).getModList().reloadFilters(); + } + this.mc.displayScreen(this.parent); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java index 0b285e5e6..447b6a2f5 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java @@ -1,8 +1,3 @@ -// -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by Fernflower decompiler) -// - package io.github.prospector.modmenu.gui; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 4da4151ce..76b0757c0 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -122,7 +122,7 @@ public void render(Minecraft mc, int mouseX, int mouseY) { } else { enabled = false; } - visible = enabled; // visible = enabled + visible = enabled; int prevColor = GLRenderer.getColor(); GLRenderer.setColor4f(1, 1, 1, 1); @@ -157,8 +157,8 @@ public void render(Minecraft mc, int int_1, int int_2) { } } }); - String showLibrariesText = i18n.translateKeyAndFormat("modmenu.showLibraries", i18n.translateKey("modmenu.showLibraries." + ModMenuConfigManager.getConfig().showLibraries())); - String sortingText = i18n.translateKeyAndFormat("modmenu.sorting", ModMenuConfigManager.getConfig().getSorting().getName()); + String showLibrariesText = ModMenuConfigManager.getConfig().getShowLibrariesDisplayString(); + String sortingText = ModMenuConfigManager.getConfig().getSortingDisplayString(); int showLibrariesWidth = fontRenderer.stringWidth(showLibrariesText) + 20; int sortingWidth = fontRenderer.stringWidth(sortingText) + 20; int filtersX; @@ -174,7 +174,7 @@ public void render(Minecraft mc, int int_1, int int_2) { @Override public void drawButton(Minecraft mc, int mouseX, int mouseY) { visible = enabled = filterOptionsShown; - this.displayString = i18n.translateKeyAndFormat("modmenu.sorting", ModMenuConfigManager.getConfig().getSorting().getName()); + this.displayString = ModMenuConfigManager.getConfig().getSortingDisplayString(); super.drawButton(mc, mouseX, mouseY); } }); @@ -182,7 +182,7 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { @Override public void drawButton(Minecraft mc, int mouseX, int mouseY) { visible = enabled = filterOptionsShown; - this.displayString = i18n.translateKeyAndFormat("modmenu.showLibraries", i18n.translateKey("modmenu.showLibraries." + ModMenuConfigManager.getConfig().showLibraries())); + this.displayString = ModMenuConfigManager.getConfig().getShowLibrariesDisplayString(); super.drawButton(mc, mouseX, mouseY); } }); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java index 89672d307..ae3837cd1 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java @@ -2,8 +2,11 @@ import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.ModListScreen; import io.github.prospector.modmenu.gui.ModMenuButtonWidget; +import io.github.prospector.modmenu.gui.ModMenuTexturedButtonWidget; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; import net.minecraft.client.gui.ScreenPause; @@ -17,15 +20,24 @@ public class MixinGuiIngameMenu extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - I18n i18n = I18n.getInstance(); - String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); - this.buttons.add(new ModMenuButtonWidget(100, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); + ModMenuConfig config = ModMenuConfigManager.getConfig(); + if (config.getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { + I18n i18n = I18n.getInstance(); + String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); + this.buttons.add(new ModMenuButtonWidget(1000, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); + } else { + this.buttons.removeIf(b -> b.id == 10); + this.buttons.add(new ModMenuTexturedButtonWidget( + 1000, this.width / 2 - 124, this.height / 4 + 104, 20, 20, 0, 0, + "/assets/" + ModMenu.MOD_ID + "/textures/gui/open_button.png", 32, 64 + ) {}); + } } @Inject(method = "buttonClicked", at = @At("HEAD")) private void modmenu$onActionPerformed(ButtonElement button, CallbackInfo ci) { - if (button.id == 100) { - mc.displayScreen(new ModListScreen(this)); + if (button.id == 1000) { + this.mc.displayScreen(new ModListScreen(this)); } } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java index 6242108d3..6b66f7ea3 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java @@ -2,8 +2,11 @@ import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.ModListScreen; import io.github.prospector.modmenu.gui.ModMenuButtonWidget; +import io.github.prospector.modmenu.gui.ModMenuTexturedButtonWidget; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; import net.minecraft.client.gui.ScreenMainMenu; @@ -19,13 +22,23 @@ public class MixinGuiMainMenu extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - I18n i18n = I18n.getInstance(); - ButtonElement texturePackButton = this.buttons.get(2); - texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); - int newWidth = ((GuiButtonAccessor) texturePackButton).getWidth() / 2 - 1; - ((GuiButtonAccessor) texturePackButton).setWidth(newWidth); - String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); - this.buttons.add(new ModMenuButtonWidget(100, this.width / 2 + 2, texturePackButton.yPosition, newWidth, 20, buttonText)); + ModMenuConfig config = ModMenuConfigManager.getConfig(); + + if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { + I18n i18n = I18n.getInstance(); + ButtonElement texturePackButton = this.buttons.get(2); + texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); + int newWidth = ((GuiButtonAccessor) texturePackButton).getWidth() / 2 - 1; + ((GuiButtonAccessor) texturePackButton).setWidth(newWidth); + String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); + this.buttons.add(new ModMenuButtonWidget(100, this.width / 2 + 2, texturePackButton.yPosition, newWidth, 20, buttonText)); + } else { + this.buttons.removeIf(b -> b.id == 11); + this.buttons.add(new ModMenuTexturedButtonWidget( + 100, this.width / 2 - 124, this.height / 4 + 132, 20, 20, 0, 0, + "/assets/" + ModMenu.MOD_ID + "/textures/gui/open_button.png", 32, 64 + ) {}); + } } @Inject(method = "buttonClicked", at = @At("HEAD")) diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index dac142617..f254ca223 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -11,13 +11,21 @@ modmenu.authorPrefix=By %s modmenu.license=License: modmenu.search=Search for mods modmenu.toggleFilterOptions=Toggle Filter Options -modmenu.showLibraries=Libraries: %s -modmenu.showLibraries.true=Shown -modmenu.showLibraries.false=Hidden modmenu.showingMods=Showing %s Mods -modmenu.sorting=Sort: %s -modmenu.sorting.ascending=A-Z -modmenu.sorting.descending=Z-A + +modmenu.options=Mod Menu Options +option.modmenu.sorting=Sort +option.modmenu.sorting.ascending=A-Z +option.modmenu.sorting.descending=Z-A +option.modmenu.show_libraries=Libraries +option.modmenu.show_libraries.true=Shown +option.modmenu.show_libraries.false=Hidden +option.modmenu.game_menu_button_style=Game Menu +option.modmenu.game_menu_button_style.insert=Insert +option.modmenu.game_menu_button_style.icon=Icon +option.modmenu.mods_button_style=Title Screen +option.modmenu.mods_button_style.insert=Insert +option.modmenu.mods_button_style.icon=Icon modmenu.credits=Credits: modmenu.authors=Authors: diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 0175450ee..a18440fe6 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -9,6 +9,9 @@ "entrypoints": { "main": [ "io.github.prospector.modmenu.ModMenu" + ], + "modmenu": [ + "io.github.prospector.modmenu.config.ModMenuConfigIntegration" ] }, "contact": { From 3ff92078ee7f31f94e05680bf03266a1afb22956 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:23:10 +0300 Subject: [PATCH 40/59] Not too important, not too unimportant. --- .../prospector/modmenu/mixin/MixinGuiIngameMenu.java | 3 +-- .../prospector/modmenu/mixin/MixinGuiMainMenu.java | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java index ae3837cd1..66d4acd48 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java @@ -20,8 +20,7 @@ public class MixinGuiIngameMenu extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - if (config.getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { + if (ModMenuConfigManager.getConfig().getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { I18n i18n = I18n.getInstance(); String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); this.buttons.add(new ModMenuButtonWidget(1000, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java index 6b66f7ea3..c579be387 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java @@ -22,12 +22,10 @@ public class MixinGuiMainMenu extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - - if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { - I18n i18n = I18n.getInstance(); - ButtonElement texturePackButton = this.buttons.get(2); - texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); + ButtonElement texturePackButton = this.buttons.get(2); + I18n i18n = I18n.getInstance(); + texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); + if (ModMenuConfigManager.getConfig().getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { int newWidth = ((GuiButtonAccessor) texturePackButton).getWidth() / 2 - 1; ((GuiButtonAccessor) texturePackButton).setWidth(newWidth); String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); From 2118c7d9a8abc72be672e0b35b51dec58b496b00 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:25:46 +0300 Subject: [PATCH 41/59] more options --- .../modmenu/config/ModMenuConfig.java | 114 ++++++++++++++---- .../modmenu/config/ModMenuConfigScreen.java | 48 ++++++++ .../modmenu/gui/DescriptionListWidget.java | 3 +- .../prospector/modmenu/gui/ModListEntry.java | 34 +++--- .../prospector/modmenu/gui/ModListScreen.java | 2 +- .../modmenu/mixin/MixinGuiMainMenu.java | 2 + .../modmenu/util/BadgeRenderer.java | 30 +++-- .../modmenu/util/ModListSearch.java | 7 +- .../assets/modmenu/lang/bg_BG/bg_bg.json | 12 -- .../assets/modmenu/lang/de_DE/de_de.json | 8 -- .../assets/modmenu/lang/en_US/en_US.lang | 12 ++ .../assets/modmenu/lang/en_US/en_us.json | 25 ---- .../assets/modmenu/lang/et_EE/et_ee.json | 23 ---- .../assets/modmenu/lang/fi_FI/fi_fi.json | 13 -- .../assets/modmenu/lang/fr_FR/fr_fr.json | 20 --- .../assets/modmenu/lang/ja_JP/ja_jp.json | 24 ---- .../assets/modmenu/lang/pl_PL/pl_PL.lang | 12 ++ .../assets/modmenu/lang/pl_PL/pl_pl.json | 15 --- .../assets/modmenu/lang/pt_BR/pt_br.json | 12 -- .../assets/modmenu/lang/ru_RU/ru_ru.json | 25 ---- .../assets/modmenu/lang/zh_CN/zh_cn.json | 21 ---- .../assets/modmenu/lang/zh_TW/zh_tw.json | 19 --- 22 files changed, 211 insertions(+), 270 deletions(-) delete mode 100644 src/main/resources/assets/modmenu/lang/bg_BG/bg_bg.json delete mode 100644 src/main/resources/assets/modmenu/lang/de_DE/de_de.json delete mode 100644 src/main/resources/assets/modmenu/lang/en_US/en_us.json delete mode 100644 src/main/resources/assets/modmenu/lang/et_EE/et_ee.json delete mode 100644 src/main/resources/assets/modmenu/lang/fi_FI/fi_fi.json delete mode 100644 src/main/resources/assets/modmenu/lang/fr_FR/fr_fr.json delete mode 100644 src/main/resources/assets/modmenu/lang/ja_JP/ja_jp.json create mode 100644 src/main/resources/assets/modmenu/lang/pl_PL/pl_PL.lang delete mode 100644 src/main/resources/assets/modmenu/lang/pl_PL/pl_pl.json delete mode 100644 src/main/resources/assets/modmenu/lang/pt_BR/pt_br.json delete mode 100644 src/main/resources/assets/modmenu/lang/ru_RU/ru_ru.json delete mode 100644 src/main/resources/assets/modmenu/lang/zh_CN/zh_cn.json delete mode 100644 src/main/resources/assets/modmenu/lang/zh_TW/zh_tw.json diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 03daf131a..6bd655f46 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -8,8 +8,12 @@ public class ModMenuConfig { private boolean showLibraries = false; private Sorting sorting = Sorting.ASCENDING; + private boolean hideBadges = true; + private boolean easterEggs = true; private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; + private boolean translateNames = true; + private boolean translateDescriptions = true; public void toggleShowLibraries() { this.showLibraries = !this.showLibraries; @@ -17,17 +21,37 @@ public void toggleShowLibraries() { } public void toggleSortMode() { - this.sorting = Sorting.values()[(sorting.ordinal() + 1) % Sorting.values().length]; + this.sorting = next(this.sorting); + ModMenuConfigManager.save(); + } + + public void toggleHideBadges() { + this.hideBadges = !this.hideBadges; + ModMenuConfigManager.save(); + } + + public void toggleEasterEggs() { + this.easterEggs = !this.easterEggs; ModMenuConfigManager.save(); } public void toggleGameMenuButtonStyle() { - this.gameMenuButtonStyle = GameMenuButtonStyle.values()[(gameMenuButtonStyle.ordinal() + 1) % GameMenuButtonStyle.values().length]; + this.gameMenuButtonStyle = next(this.gameMenuButtonStyle); ModMenuConfigManager.save(); } public void toggleModsButtonStyle() { - this.modsButtonStyle = ModsButtonStyle.values()[(modsButtonStyle.ordinal() + 1) % ModsButtonStyle.values().length]; + this.modsButtonStyle = next(this.modsButtonStyle); + ModMenuConfigManager.save(); + } + + public void toggleTranslateNames() { + this.translateNames = !this.translateNames; + ModMenuConfigManager.save(); + } + + public void toggleTranslateDescriptions() { + this.translateDescriptions = !this.translateDescriptions; ModMenuConfigManager.save(); } @@ -39,6 +63,14 @@ public Sorting getSorting() { return sorting; } + public boolean getHideBadges() { + return hideBadges; + } + + public boolean getEasterEggs() { + return easterEggs; + } + public GameMenuButtonStyle getGameMenuButtonStyle() { return gameMenuButtonStyle; } @@ -47,29 +79,64 @@ public ModsButtonStyle getModsButtonStyle() { return modsButtonStyle; } + public boolean getTranslateNames() { + return translateNames; + } + + public boolean getTranslateDescriptions() { + return translateDescriptions; + } + public String getSortingDisplayString() { - I18n i18n = I18n.getInstance(); - return i18n.translateKey("option.modmenu.sorting") + ": " + this.sorting.getName(); + return optionString("sorting", this.sorting.getName()); } public String getShowLibrariesDisplayString() { - I18n i18n = I18n.getInstance(); - String librariesValueKey = this.showLibraries ? "option.modmenu.show_libraries.true" : "option.modmenu.show_libraries.false"; - return i18n.translateKey("option.modmenu.show_libraries") + ": " + i18n.translateKey(librariesValueKey); + return optionString("show_libraries", I18n.getInstance().translateKey("option.modmenu.show_libraries." + this.showLibraries)); + } + + public String getHideBadgesDisplayString() { + return optionString("hide_badges", I18n.getInstance().translateKey("option.modmenu.hide_badges." + this.hideBadges)); + } + + public String getEasterEggsDisplayString() { + return optionString("easter_eggs", I18n.getInstance().translateKey("option.modmenu.easter_eggs." + this.easterEggs)); } public String getModsButtonStyleDisplayString() { - I18n i18n = I18n.getInstance(); - return i18n.translateKey("option.modmenu.mods_button_style") + ": " + this.modsButtonStyle.getName(); + return optionString("mods_button_style", this.modsButtonStyle.getName()); } public String getGameMenuButtonStyleDisplayString() { - I18n i18n = I18n.getInstance(); - return i18n.translateKey("option.modmenu.game_menu_button_style") + ": " + this.gameMenuButtonStyle.getName(); + return optionString("game_menu_button_style", this.gameMenuButtonStyle.getName()); + } + + public String getTranslateNamesDisplayString() { + return optionString("translate_names", I18n.getInstance().translateKey("option.modmenu.translate_names." + this.translateNames)); + } + + public String getTranslateDescriptionsDisplayString() { + return optionString("translate_descriptions", I18n.getInstance().translateKey("option.modmenu.translate_descriptions." + this.translateDescriptions)); + } + + private static > T next(T current) { + T[] values = current.getDeclaringClass().getEnumConstants(); + return values[(current.ordinal() + 1) % values.length]; + } + + private static String optionString(String optionKey, String valueName) { + return I18n.getInstance().translateKey("option.modmenu." + optionKey) + ": " + valueName; + } + + private interface OptionEnum { + String getKey(); + default String getName() { + return I18n.getInstance().translateKey(getKey()); + } } - public enum Sorting { - ASCENDING(Comparator.comparing(modContainer -> modContainer.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER), "option.modmenu.sorting.ascending"), + public enum Sorting implements OptionEnum { + ASCENDING(Comparator.comparing(mod -> mod.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER), "option.modmenu.sorting.ascending"), DESCENDING(ASCENDING.getComparator().reversed(), "option.modmenu.sorting.descending"); final Comparator comparator; @@ -84,12 +151,13 @@ public Comparator getComparator() { return comparator; } - public String getName() { - return I18n.getInstance().translateKey(key); + @Override + public String getKey() { + return key; } } - public enum ModsButtonStyle { + public enum ModsButtonStyle implements OptionEnum { INSERT("option.modmenu.mods_button_style.insert"), ICON("option.modmenu.mods_button_style.icon"); @@ -99,12 +167,13 @@ public enum ModsButtonStyle { this.key = key; } - public String getName() { - return I18n.getInstance().translateKey(key); + @Override + public String getKey() { + return key; } } - public enum GameMenuButtonStyle { + public enum GameMenuButtonStyle implements OptionEnum { INSERT("option.modmenu.game_menu_button_style.insert"), ICON("option.modmenu.game_menu_button_style.icon"); @@ -114,8 +183,9 @@ public enum GameMenuButtonStyle { this.key = key; } - public String getName() { - return I18n.getInstance().translateKey(key); + @Override + public String getKey() { + return key; } } } \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java index 6492b4b90..77fad664d 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -64,6 +64,42 @@ public void init() { config.getGameMenuButtonStyleDisplayString() )); + this.add(new ButtonElement( + 104, + leftX, + startY + 48, + buttonWidth, + buttonHeight, + config.getHideBadgesDisplayString() + )); + + this.add(new ButtonElement( + 105, + rightX, + startY + 48, + buttonWidth, + buttonHeight, + config.getEasterEggsDisplayString() + )); + + this.add(new ButtonElement( + 106, + leftX, + startY + 72, + buttonWidth, + buttonHeight, + config.getTranslateNamesDisplayString() + )); + + this.add(new ButtonElement( + 107, + rightX, + startY + 72, + buttonWidth, + buttonHeight, + config.getTranslateDescriptionsDisplayString() + )); + int doneButtonWidth = 200; this.add(new ButtonElement( 200, @@ -101,6 +137,18 @@ protected void buttonClicked(ButtonElement button) { } else if (button.id == 103) { config.toggleGameMenuButtonStyle(); button.displayString = config.getGameMenuButtonStyleDisplayString(); + } else if (button.id == 104) { + config.toggleHideBadges(); + button.displayString = config.getHideBadgesDisplayString(); + } else if (button.id == 105) { + config.toggleEasterEggs(); + button.displayString = config.getEasterEggsDisplayString(); + } else if (button.id == 106) { + config.toggleTranslateNames(); + button.displayString = config.getTranslateNamesDisplayString(); + } else if (button.id == 107) { + config.toggleTranslateDescriptions(); + button.displayString = config.getTranslateDescriptionsDisplayString(); } else if (button.id == 200) { this.closeAndReturn(); } diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 565cbfa95..da7ffc523 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -1,6 +1,7 @@ package io.github.prospector.modmenu.gui; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.RenderUtils; import net.fabricmc.loader.api.metadata.ContactInformation; import net.fabricmc.loader.api.metadata.Person; @@ -114,7 +115,7 @@ public void render(int mouseX, int mouseY, float delta) { String translatedDesc = i18n.translateKey(descKey); String description; - if (!translatedDesc.equals(descKey)) { + if (!translatedDesc.equals(descKey) && ModMenuConfigManager.getConfig().getTranslateDescriptions()) { description = translatedDesc; } else { description = selectedEntry.getMetadata().getDescription(); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 2486ae2f9..7aba3590e 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -2,6 +2,8 @@ import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.BadgeRenderer; import io.github.prospector.modmenu.util.RenderUtils; import net.fabricmc.loader.api.FabricLoader; @@ -45,6 +47,8 @@ public ModListEntry(Minecraft mc, ModContainer container, ModListWidget list) { @Override public void render(int index, int y, int x, int rowWidth, int rowHeight, int mouseX, int mouseY, boolean isSelected, float delta) { I18n i18n = I18n.getInstance(); + ModMenuConfig config = ModMenuConfigManager.getConfig(); + x += getXOffset(); rowWidth -= getXOffset(); @@ -52,13 +56,13 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou GLRenderer.setColor4f(1, 1, 1, 1); // We LOVE random color calls this.bindIconTexture(); - internalRender(y, x); + internalRender(y, x); String id = metadata.getId(); String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; - if (!translatedName.equals(translationKey)) { + if (!translatedName.equals(translationKey) && config.getTranslateNames()) { name = translatedName; } else { name = metadata.getName(); @@ -66,14 +70,14 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou String trimmedName = name; int maxNameWidth = rowWidth - 32 - 3; FontRenderer font = this.fontRenderer; - trimmedName = ModListScreen.getString(fontRenderer, name, trimmedName, maxNameWidth); - this.drawStringNoShadow(font, trimmedName, x + 32 + 3, y + 1, 0xFFFFFF); + trimmedName = ModListScreen.getString(fontRenderer, name, trimmedName, maxNameWidth); + this.drawStringNoShadow(font, trimmedName, x + 32 + 3, y + 1, 0xFFFFFF); new BadgeRenderer(client, x + 32 + 3 + font.stringWidth(name) + 2, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); String descKey = "modmenu.descriptionTranslation." + id; String translatedDesc = i18n.translateKey(descKey); String description; - if (!translatedDesc.equals(descKey)) { + if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { description = translatedDesc; } else { description = metadata.getDescription(); @@ -91,16 +95,16 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou int maxWidth = rowWidth - 32 - 7; int maxLines = 2; int currentLine = 0; - - font = this.fontRenderer; + + font = this.fontRenderer; String[] paragraphs = description.split("\r?\n"); - + for (String paragraph : paragraphs) { if (currentLine >= maxLines) break; - + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(font, paragraph, maxWidth)) { if (currentLine >= maxLines) break; - + this.drawStringNoShadow(font, line, textX, textY + (currentLine * 9), 0x808080); currentLine++; } @@ -110,7 +114,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou GLRenderer.popFrame(); } - static void internalRender(int y, int x) { + static void internalRender(int y, int x) { GLRenderer.pushFrame(); GLRenderer.setShader(Shaders.INTERFACE); GLRenderer.enableState(State.BLEND); @@ -124,9 +128,9 @@ static void internalRender(int y, int x) { t.draw(); GLRenderer.popFrame(); - } + } - private BufferedImage createIcon() { + private BufferedImage createIcon() { try { Path path = container.getPath(metadata.getIconPath(0).orElse("assets/" + metadata.getId() + "/icon.png")); BufferedImage cached = this.list.getCachedModIcon(path); @@ -140,8 +144,8 @@ private BufferedImage createIcon() { } else if (metadata.getId().equals("java")) { path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/java_icon.png"); } else { - path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/grey_fabric_icon.png"); - } + path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/grey_fabric_icon.png"); + } } cached = this.list.getCachedModIcon(path); if (cached != null) { diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 76b0757c0..faa64991f 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -354,7 +354,7 @@ public void render(int mouseX, int mouseY, float delta) { String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; - if (!translatedName.equals(translationKey)) { + if (!translatedName.equals(translationKey) && ModMenuConfigManager.getConfig().getTranslateNames()) { name = translatedName; } else { name = metadata.getName(); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java index c579be387..5ad969541 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java @@ -24,7 +24,9 @@ public class MixinGuiMainMenu extends Screen { public void modmenu$drawMenuButton(CallbackInfo info) { ButtonElement texturePackButton = this.buttons.get(2); I18n i18n = I18n.getInstance(); + if (ModMenuConfigManager.getConfig().getEasterEggs()) { texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); + } if (ModMenuConfigManager.getConfig().getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { int newWidth = ((GuiButtonAccessor) texturePackButton).getWidth() / 2 - 1; ((GuiButtonAccessor) texturePackButton).setWidth(newWidth); diff --git a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java index 97b0bbe57..52c3d056d 100644 --- a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java +++ b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java @@ -2,6 +2,8 @@ import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.ModListScreen; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -53,23 +55,27 @@ public void draw(int mouseX, int mouseY) { drawBadge(entry.getKey(), entry.getValue().getKey(), entry.getValue().getValue(), mouseX, mouseY); } } - //noinspection MagicConstant - if (Calendar.getInstance().get(0b10) == 0b11 && Calendar.getInstance().get(0b101) == 0x1) { - if (metadata.getId().equals(new String(new byte[]{109, 111, 100, 109, 101, 110, 117}))) { - drawBadge(new String(new byte[]{-30, -100, -104, 32, 86, 105, 114, 117, 115, 32, 68, 101, 116, 101, 99, 116, 101, 100}), 0b10001000111111110010001000100010, 0b10001000011111110000100000001000, mouseX, mouseY); - } else if (metadata.getId().contains(new String(new byte[]{116, 97, 116, 101, 114}))) { - drawBadge(new String(new byte[]{116, 97, 116, 101, 114}), 0b10001000111010111011001100101011, 0b10001000100110010111000100010010, mouseX, mouseY); - } else { - drawBadge(new String(new byte[]{-30, -100, -108, 32, 98, 121, 32, 77, 99, 65, 102, 101, 101}), 0b10001000000111011111111101001000, 0b10001000000001110110100100001110, mouseX, mouseY); + if (ModMenuConfigManager.getConfig().getEasterEggs()) { + //noinspection MagicConstant + if (Calendar.getInstance().get(0b10) == 0b11 && Calendar.getInstance().get(0b101) == 0x1) { + if (metadata.getId().equals(new String(new byte[]{109, 111, 100, 109, 101, 110, 117}))) { + drawBadge(new String(new byte[]{-30, -100, -104, 32, 86, 105, 114, 117, 115, 32, 68, 101, 116, 101, 99, 116, 101, 100}), 0b10001000111111110010001000100010, 0b10001000011111110000100000001000, mouseX, mouseY); + } else if (metadata.getId().contains(new String(new byte[]{116, 97, 116, 101, 114}))) { + drawBadge(new String(new byte[]{116, 97, 116, 101, 114}), 0b10001000111010111011001100101011, 0b10001000100110010111000100010010, mouseX, mouseY); + } else { + drawBadge(new String(new byte[]{-30, -100, -108, 32, 98, 121, 32, 77, 99, 65, 102, 101, 101}), 0b10001000000111011111111101001000, 0b10001000000001110110100100001110, mouseX, mouseY); + } } } } public void drawBadge(String text, int outlineColor, int fillColor, int mouseX, int mouseY) { - int width = client.font.stringWidth(text) + 6; - if (badgeX + width < badgeMax) { - RenderUtils.INSTANCE.drawBadge(screen, badgeX, badgeY, width, text, outlineColor, fillColor, 0xCACACA); - badgeX += width + 3; + if (!ModMenuConfigManager.getConfig().getHideBadges()) { + int width = client.font.stringWidth(text) + 6; + if (badgeX + width < badgeMax) { + RenderUtils.INSTANCE.drawBadge(screen, badgeX, badgeY, width, text, outlineColor, fillColor, 0xCACACA); + badgeX += width + 3; + } } } diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index d9dd0f396..1ecf7df52 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -2,6 +2,8 @@ import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.ModListScreen; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -34,14 +36,15 @@ private static boolean passesFilters(ModListScreen screen, ModContainer containe //Some basic search, could do with something more advanced but this will do for now I18n i18n = I18n.getInstance(); + ModMenuConfig config = ModMenuConfigManager.getConfig(); String nameTranslation = "modmenu.nameTranslation." + modId; String translatedName = i18n.translateKey(nameTranslation); - String modName = !translatedName.equals(nameTranslation) ? translatedName : metadata.getName(); + String modName = !translatedName.equals(nameTranslation) && config.getTranslateNames() ? translatedName : metadata.getName(); String descriptionTranslation = "modmenu.descriptionTranslation." + modId; String translatedDescription = i18n.translateKey(descriptionTranslation); - String modDescription = !translatedDescription.equals(descriptionTranslation) ? translatedDescription : metadata.getDescription(); + String modDescription = !translatedDescription.equals(descriptionTranslation) && config.getTranslateDescriptions() ? translatedDescription : metadata.getDescription(); if (modName.toLowerCase(Locale.ROOT).contains(query) // Search mod name || modDescription.toLowerCase(Locale.ROOT).contains(query) // Search mod description diff --git a/src/main/resources/assets/modmenu/lang/bg_BG/bg_bg.json b/src/main/resources/assets/modmenu/lang/bg_BG/bg_bg.json deleted file mode 100644 index 1aaed5fb7..000000000 --- a/src/main/resources/assets/modmenu/lang/bg_BG/bg_bg.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "modmenu.title": "Модификации", - "modmenu.loaded": "(%s заредени)", - "modmenu.config": "Редактирай конфигурация", - "modmenu.modsFolder": "Отвори папка с модификации", - "modmenu.configsFolder": "Отвори папка с конфигурации", - "modmenu.configure": "Конфигурирай...", - "modmenu.modIdToolTip": "ID на модификацията: %s", - "modmenu.website": "Уебсайт", - "modmenu.issues": "Проблеми", - "modmenu.authorPrefix": "От %s" -} \ No newline at end of file diff --git a/src/main/resources/assets/modmenu/lang/de_DE/de_de.json b/src/main/resources/assets/modmenu/lang/de_DE/de_de.json deleted file mode 100644 index 49652ebf4..000000000 --- a/src/main/resources/assets/modmenu/lang/de_DE/de_de.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "modmenu.title": "Mods", - "modmenu.loaded": "(%s geladen)", - "modmenu.config": "Ändere Konfiguration", - "modmenu.modsFolder": "Öffne Mods-Ordner", - "modmenu.configsFolder": "Öffne Konfigurationen-Ordner", - "modmenu.configure": "Konfiguriere..." -} diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang index f254ca223..a80a3d42c 100644 --- a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US/en_US.lang @@ -26,6 +26,18 @@ option.modmenu.game_menu_button_style.icon=Icon option.modmenu.mods_button_style=Title Screen option.modmenu.mods_button_style.insert=Insert option.modmenu.mods_button_style.icon=Icon +option.modmenu.hide_badges=Mod Badges +option.modmenu.hide_badges.true=Hidden +option.modmenu.hide_badges.false=Shown +option.modmenu.easter_eggs=Easter Eggs +option.modmenu.easter_eggs.true=Enabled +option.modmenu.easter_eggs.false=Disabled +option.modmenu.translate_names=Names +option.modmenu.translate_names.true=Localized +option.modmenu.translate_names.false=Not Localized +option.modmenu.translate_descriptions=Descriptions +option.modmenu.translate_descriptions.true=Localized +option.modmenu.translate_descriptions.false=Not Localized modmenu.credits=Credits: modmenu.authors=Authors: diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_us.json b/src/main/resources/assets/modmenu/lang/en_US/en_us.json deleted file mode 100644 index 3fce4052f..000000000 --- a/src/main/resources/assets/modmenu/lang/en_US/en_us.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "modmenu.title": "Mods", - "modmenu.loaded": "(%s Loaded)", - "modmenu.config": "Edit Config", - "modmenu.modsFolder": "Open Mods Folder", - "modmenu.configsFolder": "Open Configs Folder", - "modmenu.configure": "Configure...", - "modmenu.modIdToolTip": "Mod ID: %s", - "modmenu.website": "Website", - "modmenu.issues": "Issues", - "modmenu.authorPrefix": "By %s", - "modmenu.search": "Search for mods", - "modmenu.clientsideOnly": "Client", - "modmenu.library": "Library", - "modmenu.toggleFilterOptions": "Toggle Filter Options", - "modmenu.showLibraries": "Libraries: %s", - "modmenu.showLibraries.true": "Shown", - "modmenu.showLibraries.false": "Hidden", - "modmenu.showingMods": "Showing %s Mods", - "modmenu.sorting": "Sort: %s", - "modmenu.sorting.ascending": "A-Z", - "modmenu.sorting.decending": "Z-A", - "modmenu.minecraft": "Minecraft", - "modmenu.forge": "Forge" -} diff --git a/src/main/resources/assets/modmenu/lang/et_EE/et_ee.json b/src/main/resources/assets/modmenu/lang/et_EE/et_ee.json deleted file mode 100644 index 46d6edf7d..000000000 --- a/src/main/resources/assets/modmenu/lang/et_EE/et_ee.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "modmenu.title": "Modid", - "modmenu.loaded": "(%s laaditud)", - "modmenu.config": "Muuda seadistust", - "modmenu.modsFolder": "Ava modide kaust", - "modmenu.configsFolder": "Ava seadistuste kaust", - "modmenu.configure": "Seadista...", - "modmenu.modIdToolTip": "Modi ID: %s", - "modmenu.website": "Veebileht", - "modmenu.issues": "Veahaldus", - "modmenu.authorPrefix": "Autorilt %s", - "modmenu.search": "Otsi mode", - "modmenu.clientsideOnly": "Klient", - "modmenu.library": "Teek", - "modmenu.toggleFilterOptions": "Lülita filtreerimisvalikuid", - "modmenu.showLibraries": "Teegid: %s", - "modmenu.showLibraries.true": "kuvatud", - "modmenu.showLibraries.false": "peidetud", - "modmenu.showingMods": "Kuvan %s modi", - "modmenu.sorting": "Jrk: %s", - "modmenu.sorting.ascending": "A-Z", - "modmenu.sorting.decending": "Z-A" -} diff --git a/src/main/resources/assets/modmenu/lang/fi_FI/fi_fi.json b/src/main/resources/assets/modmenu/lang/fi_FI/fi_fi.json deleted file mode 100644 index d62cf4819..000000000 --- a/src/main/resources/assets/modmenu/lang/fi_FI/fi_fi.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "modmenu.title": "Modit", - "modmenu.loaded": "(%s ladattu)", - "modmenu.config": "Muuta asetuksia", - "modmenu.modsFolder": "Avaa modikansio", - "modmenu.configsFolder": "Avaa asetuskansio", - "modmenu.configure": "Asetukset...", - "modmenu.modIdToolTip": "Modi-ID: %s", - "modmenu.website": "Kotisivu", - "modmenu.issues": "Ongelmat", - "modmenu.authorPrefix": "Tehnyt %s", - "modmenu.search": "Etsi modeja" -} diff --git a/src/main/resources/assets/modmenu/lang/fr_FR/fr_fr.json b/src/main/resources/assets/modmenu/lang/fr_FR/fr_fr.json deleted file mode 100644 index 48564175e..000000000 --- a/src/main/resources/assets/modmenu/lang/fr_FR/fr_fr.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "modmenu.title": "Mods", - "modmenu.loaded": "(%s chargés)", - "modmenu.config": "Modifier la config", - "modmenu.modsFolder": "Ouvrir le dossier des mods", - "modmenu.configsFolder": "Ouvrir le dossier des configs", - "modmenu.configure": "Configurer...", - "modmenu.modIdToolTip": "ID du mod : %s", - "modmenu.website": "Site web", - "modmenu.issues": "Problèmes", - "modmenu.authorPrefix": "Par %s", - "modmenu.search": "Rechercher des mods", - "modmenu.clientsideOnly": "Client", - "modmenu.library": "Librairie", - "modmenu.toggleFilterOptions": "Basculer options de filtre", - "modmenu.showLibraries": "Librairies : %s", - "modmenu.showLibraries.true": "montrées", - "modmenu.showLibraries.false": "cachées", - "modmenu.showingMods": "%s mods affichés" -} diff --git a/src/main/resources/assets/modmenu/lang/ja_JP/ja_jp.json b/src/main/resources/assets/modmenu/lang/ja_JP/ja_jp.json deleted file mode 100644 index 5d2faf2f5..000000000 --- a/src/main/resources/assets/modmenu/lang/ja_JP/ja_jp.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "modmenu.title": "Mods", - "modmenu.loaded": "(%s個 読込済み)", - "modmenu.config": "設定の編集", - "modmenu.modsFolder": "modsフォルダーを開く", - "modmenu.configsFolder": "configフォルダーを開く", - "modmenu.configure": "設定…", - "modmenu.modIdToolTip": "Mod ID: %s", - "modmenu.website": "ウェブサイト", - "modmenu.issues": "バグを報告", - "modmenu.authorPrefix": "制作: %s", - "modmenu.search": "Modを検索", - "modmenu.clientsideOnly": "クライアント", - "modmenu.library": "前提mod", - "modmenu.toggleFilterOptions": "フィルター設定の切り替え", - "modmenu.showLibraries": "前提mod: %s", - "modmenu.showLibraries.true": "表示", - "modmenu.showLibraries.false": "非表示", - "modmenu.showingMods": "%s個のmodを表示中", - "modmenu.sorting": "表示順: %s", - "modmenu.sorting.ascending": "昇順(A-Z)", - "modmenu.sorting.decending": "降順(Z-A)", - "modmenu.minecraft": "Minecraft" -} diff --git a/src/main/resources/assets/modmenu/lang/pl_PL/pl_PL.lang b/src/main/resources/assets/modmenu/lang/pl_PL/pl_PL.lang new file mode 100644 index 000000000..5b3238112 --- /dev/null +++ b/src/main/resources/assets/modmenu/lang/pl_PL/pl_PL.lang @@ -0,0 +1,12 @@ +modmenu.title=Mody +modmenu.loaded=(%s Załadowanych) +modmenu.config=Edytuj konfigurację +modmenu.modsFolder=Otwórz folder z modami +modmenu.configsFolder=Otwórz folder z plikami kontiguracyjnymi +modmenu.configure=Konfiguruję... +modmenu.modIdToolTip=Mod ID: %s +modmenu.website=Strona +modmenu.issues=Problemy +modmenu.authorPrefix=Stworzone przez %s +modmenu.search=Szukam modów... +modmenu.clientsideOnly=Client diff --git a/src/main/resources/assets/modmenu/lang/pl_PL/pl_pl.json b/src/main/resources/assets/modmenu/lang/pl_PL/pl_pl.json deleted file mode 100644 index 8ce2ef341..000000000 --- a/src/main/resources/assets/modmenu/lang/pl_PL/pl_pl.json +++ /dev/null @@ -1,15 +0,0 @@ - -{ - "modmenu.title": "Mody", - "modmenu.loaded": "(%s Załadowanych)", - "modmenu.config": "Edytuj konfigurację", - "modmenu.modsFolder": "Otwórz folder z modami", - "modmenu.configsFolder": "Otwórz folder z plikami kontiguracyjnymi", - "modmenu.configure": "Konfiguruję...", - "modmenu.modIdToolTip": "Mod ID: %s", - "modmenu.website": "Strona", - "modmenu.issues": "Problemy", - "modmenu.authorPrefix": "Stworzone przez %s", - "modmenu.search": "Szukam modów...", - "modmenu.clientsideOnly": "Client" -} diff --git a/src/main/resources/assets/modmenu/lang/pt_BR/pt_br.json b/src/main/resources/assets/modmenu/lang/pt_BR/pt_br.json deleted file mode 100644 index 9f5c719dd..000000000 --- a/src/main/resources/assets/modmenu/lang/pt_BR/pt_br.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "modmenu.title": "Mods", - "modmenu.loaded": "(%s Carregados)", - "modmenu.config": "Editar Config", - "modmenu.modsFolder": "Abrir pasta de mods", - "modmenu.configsFolder": "Abrir pasta de Config", - "modmenu.configure": "Configuração...", - "modmenu.modIdToolTip": "Mod ID: %s", - "modmenu.website": "Website", - "modmenu.issues": "Problemas", - "modmenu.authorPrefix": "Por %s" -} diff --git a/src/main/resources/assets/modmenu/lang/ru_RU/ru_ru.json b/src/main/resources/assets/modmenu/lang/ru_RU/ru_ru.json deleted file mode 100644 index 5621d5894..000000000 --- a/src/main/resources/assets/modmenu/lang/ru_RU/ru_ru.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "modmenu.title": "Моды", - "modmenu.loaded": "(загружено: %s)", - "modmenu.config": "Изменить настройки", - "modmenu.modsFolder": "Открыть папку модов", - "modmenu.configsFolder": "Открыть папку настроек", - "modmenu.configure": "Настроить...", - "modmenu.modIdToolTip": "ID мода: %s", - "modmenu.website": "Сайт", - "modmenu.issues": "Проблемы/Вопросы", - "modmenu.authorPrefix": "От %s", - "modmenu.search": "Поиск модов", - "modmenu.clientsideOnly": "Клиент", - "modmenu.library": "Библиотека", - "modmenu.toggleFilterOptions": "Переключить параметры фильтра", - "modmenu.showLibraries": "Библиотеки: %s", - "modmenu.showLibraries.true": "Показано", - "modmenu.showLibraries.false": "Скрыто", - "modmenu.showingMods": "Показано модов: %s", - "modmenu.sorting": "Сортировка: %s", - "modmenu.sorting.ascending": "A-Z", - "modmenu.sorting.decending": "Z-A", - "modmenu.minecraft": "Minecraft", - "modmenu.forge": "Forge" -} diff --git a/src/main/resources/assets/modmenu/lang/zh_CN/zh_cn.json b/src/main/resources/assets/modmenu/lang/zh_CN/zh_cn.json deleted file mode 100644 index a3b774aaa..000000000 --- a/src/main/resources/assets/modmenu/lang/zh_CN/zh_cn.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "modmenu.title": "模组", - "modmenu.loaded": "(已载入%s个)", - "modmenu.config": "编辑配置", - "modmenu.modsFolder": "开启模组文件夹", - "modmenu.configsFolder": "开启配置文件夹", - "modmenu.configure": "配置...", - "modmenu.modIdToolTip": "模组 ID: %s", - "modmenu.website": "网站", - "modmenu.issues": "反馈", - "modmenu.authorPrefix": "作者: %s", - "modmenu.search": "搜索模组", - "modmenu.clientsideOnly": "客户端", - "modmenu.library": "运行库", - "modmenu.toggleFilterOptions": "切换过滤选项", - "modmenu.showLibraries": "运行库: %s", - "modmenu.showLibraries.true": "显示", - "modmenu.showLibraries.false": "隐藏", - "modmenu.showingMods": "显示 %s 个模组", - "modmenu.sorting": "排序方式: %s" -} diff --git a/src/main/resources/assets/modmenu/lang/zh_TW/zh_tw.json b/src/main/resources/assets/modmenu/lang/zh_TW/zh_tw.json deleted file mode 100644 index 9eb629814..000000000 --- a/src/main/resources/assets/modmenu/lang/zh_TW/zh_tw.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "modmenu.title": "模組", - "modmenu.loaded": "(已載入%s個)", - "modmenu.config": "編輯設置", - "modmenu.modsFolder": "開啟模組資料夾", - "modmenu.configsFolder": "開啟設置資料夾", - "modmenu.configure": "設置...", - "modmenu.modIdToolTip": "模組 ID: %s", - "modmenu.website": "網站", - "modmenu.issues": "反饋", - "modmenu.authorPrefix": "作者: %s", - "modmenu.search": "搜索模组", - "modmenu.clientsideOnly": "玩家端", - "modmenu.toggleFilterOptions": "切換過濾選項", - "modmenu.showLibraries.true": "顯示", - "modmenu.showLibraries.false": "隱藏", - "modmenu.showingMods": "隱藏 %s 個模組", - "modmenu.sorting": "排序方式: %s" -} From 952123aeba7f6afacd9ed1e7e599d32ad370b1d0 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:01:25 +0300 Subject: [PATCH 42/59] langs --- .../java/io/github/prospector/modmenu/mixin/MixinI18n.java | 4 ++-- src/main/resources/assets/modmenu/lang/{bg_BG => }/bg_BG.lang | 0 src/main/resources/assets/modmenu/lang/{de_DE => }/de_DE.lang | 0 src/main/resources/assets/modmenu/lang/{en_US => }/en_US.lang | 0 src/main/resources/assets/modmenu/lang/{et_EE => }/et_EE.lang | 0 src/main/resources/assets/modmenu/lang/{fi_FI => }/fi_FI.lang | 0 src/main/resources/assets/modmenu/lang/{fr_FR => }/fr_FR.lang | 0 src/main/resources/assets/modmenu/lang/{ja_JP => }/ja_JP.lang | 0 src/main/resources/assets/modmenu/lang/{pl_PL => }/pl_PL.lang | 0 src/main/resources/assets/modmenu/lang/{pt_BR => }/pt_BR.lang | 0 src/main/resources/assets/modmenu/lang/{ru_RU => }/ru_RU.lang | 0 src/main/resources/assets/modmenu/lang/{zh_CN => }/zh_CN.lang | 0 src/main/resources/assets/modmenu/lang/{zh_TW => }/zh_TW.lang | 0 13 files changed, 2 insertions(+), 2 deletions(-) rename src/main/resources/assets/modmenu/lang/{bg_BG => }/bg_BG.lang (100%) rename src/main/resources/assets/modmenu/lang/{de_DE => }/de_DE.lang (100%) rename src/main/resources/assets/modmenu/lang/{en_US => }/en_US.lang (100%) rename src/main/resources/assets/modmenu/lang/{et_EE => }/et_EE.lang (100%) rename src/main/resources/assets/modmenu/lang/{fi_FI => }/fi_FI.lang (100%) rename src/main/resources/assets/modmenu/lang/{fr_FR => }/fr_FR.lang (100%) rename src/main/resources/assets/modmenu/lang/{ja_JP => }/ja_JP.lang (100%) rename src/main/resources/assets/modmenu/lang/{pl_PL => }/pl_PL.lang (100%) rename src/main/resources/assets/modmenu/lang/{pt_BR => }/pt_BR.lang (100%) rename src/main/resources/assets/modmenu/lang/{ru_RU => }/ru_RU.lang (100%) rename src/main/resources/assets/modmenu/lang/{zh_CN => }/zh_CN.lang (100%) rename src/main/resources/assets/modmenu/lang/{zh_TW => }/zh_TW.lang (100%) diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java index f82548d3f..f7029389e 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java @@ -30,7 +30,7 @@ public static InputStream getResourceAsStream(String path) { ) private void modmenu$addLangEntries(String languageCode, CallbackInfo ci) { Properties entries = ((LanguageAccessor) currentLanguage).getEntries(); - String lang = "/lang/modmenu/" + currentLanguage.getId() + ".lang"; + String lang = "/assets/modmenu/lang/" + currentLanguage.getId() + ".lang"; try (InputStream stream = getResourceAsStream(lang)) { if (stream != null) { InputStreamReader r = new InputStreamReader(stream, StandardCharsets.UTF_8); @@ -39,7 +39,7 @@ public static InputStream getResourceAsStream(String path) { } catch (IOException e) { ModMenu.LOGGER.error("Failed to load {} language.", currentLanguage.getId(), e); } - String defaultLang = "/lang/modmenu/en_US.lang"; + String defaultLang = "/assets/modmenu/lang/en_US.lang"; try (InputStream stream = getResourceAsStream(defaultLang)) { if (stream != null) { InputStreamReader r = new InputStreamReader(stream, StandardCharsets.UTF_8); diff --git a/src/main/resources/assets/modmenu/lang/bg_BG/bg_BG.lang b/src/main/resources/assets/modmenu/lang/bg_BG.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/bg_BG/bg_BG.lang rename to src/main/resources/assets/modmenu/lang/bg_BG.lang diff --git a/src/main/resources/assets/modmenu/lang/de_DE/de_DE.lang b/src/main/resources/assets/modmenu/lang/de_DE.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/de_DE/de_DE.lang rename to src/main/resources/assets/modmenu/lang/de_DE.lang diff --git a/src/main/resources/assets/modmenu/lang/en_US/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/en_US/en_US.lang rename to src/main/resources/assets/modmenu/lang/en_US.lang diff --git a/src/main/resources/assets/modmenu/lang/et_EE/et_EE.lang b/src/main/resources/assets/modmenu/lang/et_EE.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/et_EE/et_EE.lang rename to src/main/resources/assets/modmenu/lang/et_EE.lang diff --git a/src/main/resources/assets/modmenu/lang/fi_FI/fi_FI.lang b/src/main/resources/assets/modmenu/lang/fi_FI.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/fi_FI/fi_FI.lang rename to src/main/resources/assets/modmenu/lang/fi_FI.lang diff --git a/src/main/resources/assets/modmenu/lang/fr_FR/fr_FR.lang b/src/main/resources/assets/modmenu/lang/fr_FR.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/fr_FR/fr_FR.lang rename to src/main/resources/assets/modmenu/lang/fr_FR.lang diff --git a/src/main/resources/assets/modmenu/lang/ja_JP/ja_JP.lang b/src/main/resources/assets/modmenu/lang/ja_JP.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/ja_JP/ja_JP.lang rename to src/main/resources/assets/modmenu/lang/ja_JP.lang diff --git a/src/main/resources/assets/modmenu/lang/pl_PL/pl_PL.lang b/src/main/resources/assets/modmenu/lang/pl_PL.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/pl_PL/pl_PL.lang rename to src/main/resources/assets/modmenu/lang/pl_PL.lang diff --git a/src/main/resources/assets/modmenu/lang/pt_BR/pt_BR.lang b/src/main/resources/assets/modmenu/lang/pt_BR.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/pt_BR/pt_BR.lang rename to src/main/resources/assets/modmenu/lang/pt_BR.lang diff --git a/src/main/resources/assets/modmenu/lang/ru_RU/ru_RU.lang b/src/main/resources/assets/modmenu/lang/ru_RU.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/ru_RU/ru_RU.lang rename to src/main/resources/assets/modmenu/lang/ru_RU.lang diff --git a/src/main/resources/assets/modmenu/lang/zh_CN/zh_CN.lang b/src/main/resources/assets/modmenu/lang/zh_CN.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/zh_CN/zh_CN.lang rename to src/main/resources/assets/modmenu/lang/zh_CN.lang diff --git a/src/main/resources/assets/modmenu/lang/zh_TW/zh_TW.lang b/src/main/resources/assets/modmenu/lang/zh_TW.lang similarity index 100% rename from src/main/resources/assets/modmenu/lang/zh_TW/zh_TW.lang rename to src/main/resources/assets/modmenu/lang/zh_TW.lang From 7e7dc0e4fcd05788c8738f705424593ef5e14908 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:26:52 +0300 Subject: [PATCH 43/59] unknown_icon.png --- .../prospector/modmenu/gui/ModListEntry.java | 2 +- .../assets/modmenu/grey_fabric_icon.png | Bin 1995 -> 0 bytes .../resources/assets/modmenu/unknown_icon.png | Bin 0 -> 98 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/main/resources/assets/modmenu/grey_fabric_icon.png create mode 100644 src/main/resources/assets/modmenu/unknown_icon.png diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 7aba3590e..cf505b6e7 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -144,7 +144,7 @@ private BufferedImage createIcon() { } else if (metadata.getId().equals("java")) { path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/java_icon.png"); } else { - path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/grey_fabric_icon.png"); + path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/unknown_icon.png"); } } cached = this.list.getCachedModIcon(path); diff --git a/src/main/resources/assets/modmenu/grey_fabric_icon.png b/src/main/resources/assets/modmenu/grey_fabric_icon.png deleted file mode 100644 index e0b949d9442cf161377e4bb24d99eb0b70f26285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1995 zcmb_dQD_`R7+wuF$!Tk#sg{VkY(*5E+nv4L+1s&an%pJLsTZ%8gQkLz-PyUjC3idP z?k0CR1%rK2(Ffl|6hyGT73_l+3bwT%p@{mRv`UepmEuz$tZxd=-d(P-BsCNd_I7sW zpYQ+v|Ns7(H;S_dcP7RXf*|b7&*Vz{EymB#4*nmu+t>MP*q=EP3c}sHI(zR2kCDC;<(qX-z6#Oi$ zxijs6&b4RDq`gQ?M;d=x+~2|+pg|)`Y&Gg$h+7${gNyk%o|YxCV-hW9q>0#}IA17= zSr$-HPa;5|rizA{gh(+}?MYEppdl-U3{yaXSV1^tioKu2y#-DMmvYlRU;Ha0)uPD9 zvfOMolg(6;1yvcErYS3`tg3)pfN;f&tQPRXed`E08j`^EBbRw%jA+?xDauHkX*Yz1 z--q?WUYdBqU>O~>9g?(GU#v&Hh*cQ8fD_r}?P9qIMK4Fts`5iIg88_r* zUg`DH9DS)bs=H!GKiDF1Ut}Z_3xYaQZ`$1?AKUWW+uy8F4tfifbiIeH5;d)WPE>jB zwj)G&EB>Lid~5auIw^xHIzSH3n?~bmDo6o3f_&Dn(&-e>f0x##>~*Yg9d|W;o9uh= zq-J?l%8y!Ey8U>zr_P(=*{Tu$*LdXkknKuMzFCExwG9U{FK1_ppYrjrf4%UJ{sl!n zo~nRfKmPX#%&)Fb^0|rf{JGyQoE<(R-g8%RFhHOCL)RYq;Oq~jv9+nOw+?-G@!a^( zm#_YH^y6K^rEg9?A^iMu>yL3^&xIG@*xG}urNs2Wr^AaR1yhdw;(I4*Wg(@WAWPZjX4upI2K? zEsUyjub>YHj~uEGoSAs!-mhO=z3;?F7azTR{`9YdC$1Y`yz_QKj<4JN)NJnf}1cQ`QfM6mvIX0&4z t=nQ7y6JQER=HO!BjA7z%SR&T6kCh?mX~|aJ&~ko|W=~f?mvv4FO#rA$6|(>U literal 0 HcmV?d00001 From 60ffabedc1af3594ca2478bdd7c4de4691222ba2 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:24:15 +0300 Subject: [PATCH 44/59] more --- .../modmenu/config/ModMenuConfig.java | 96 ++++++++++++++++-- .../modmenu/config/ModMenuConfigScreen.java | 85 ++++++++++++++-- .../modmenu/gui/DescriptionListWidget.java | 13 ++- .../prospector/modmenu/gui/ModListEntry.java | 77 +++++++++++++- .../prospector/modmenu/gui/ModListScreen.java | 51 ++++++---- .../prospector/modmenu/gui/ModListWidget.java | 34 ++++++- .../modmenu/mixin/MixinGuiMainMenu.java | 7 +- .../prospector/modmenu/util/DrawingUtil.java | 51 ++++++++++ .../resources/assets/modmenu/lang/en_US.lang | 30 ++++-- .../textures/gui/mod_configuration.png | Bin 0 -> 279 bytes .../modmenu/textures/gui/open_button.png | Bin 0 -> 1492 bytes 11 files changed, 390 insertions(+), 54 deletions(-) create mode 100644 src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java create mode 100644 src/main/resources/assets/modmenu/textures/gui/mod_configuration.png create mode 100644 src/main/resources/assets/modmenu/textures/gui/open_button.png diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 6bd655f46..fa7492442 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -6,22 +6,33 @@ import java.util.Comparator; public class ModMenuConfig { - private boolean showLibraries = false; private Sorting sorting = Sorting.ASCENDING; - private boolean hideBadges = true; + private boolean showLibraries = false; + private boolean hideConfigButtons = false; + private boolean hideBadges = false; + private boolean hideModLinks = false; + private boolean hideModCredits = false; + private boolean hideModLicense = false; private boolean easterEggs = true; private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; + private boolean randomJavaColors = true; private boolean translateNames = true; private boolean translateDescriptions = true; + private boolean quickConfigure = true; + + public void toggleSortMode() { + this.sorting = next(this.sorting); + ModMenuConfigManager.save(); + } public void toggleShowLibraries() { this.showLibraries = !this.showLibraries; ModMenuConfigManager.save(); } - public void toggleSortMode() { - this.sorting = next(this.sorting); + public void toggleHideConfigButtons() { + this.hideConfigButtons = !this.hideConfigButtons; ModMenuConfigManager.save(); } @@ -30,6 +41,21 @@ public void toggleHideBadges() { ModMenuConfigManager.save(); } + public void toggleHideModLinks() { + this.hideModLinks = !this.hideModLinks; + ModMenuConfigManager.save(); + } + + public void toggleHideModCredits() { + this.hideModCredits = !this.hideModCredits; + ModMenuConfigManager.save(); + } + + public void toggleHideModLicense() { + this.hideModLicense = !this.hideModLicense; + ModMenuConfigManager.save(); + } + public void toggleEasterEggs() { this.easterEggs = !this.easterEggs; ModMenuConfigManager.save(); @@ -45,6 +71,11 @@ public void toggleModsButtonStyle() { ModMenuConfigManager.save(); } + public void toggleRandomJavaColors() { + this.randomJavaColors = !this.randomJavaColors; + ModMenuConfigManager.save(); + } + public void toggleTranslateNames() { this.translateNames = !this.translateNames; ModMenuConfigManager.save(); @@ -55,18 +86,39 @@ public void toggleTranslateDescriptions() { ModMenuConfigManager.save(); } - public boolean showLibraries() { - return showLibraries; + public void toggleQuickConfigure() { + this.quickConfigure = !this.quickConfigure; + ModMenuConfigManager.save(); } public Sorting getSorting() { return sorting; } + public boolean getShowLibraries() { + return showLibraries; + } + + public boolean getHideConfigButtons() { + return hideConfigButtons; + } + public boolean getHideBadges() { return hideBadges; } + public boolean getHideModLinks() { + return hideModLinks; + } + + public boolean getHideModCredits() { + return hideModCredits; + } + + public boolean getHideModLicense() { + return hideModLicense; + } + public boolean getEasterEggs() { return easterEggs; } @@ -79,6 +131,10 @@ public ModsButtonStyle getModsButtonStyle() { return modsButtonStyle; } + public boolean getRandomJavaColors() { + return randomJavaColors; + } + public boolean getTranslateNames() { return translateNames; } @@ -87,6 +143,10 @@ public boolean getTranslateDescriptions() { return translateDescriptions; } + public boolean getQuickConfigure() { + return quickConfigure; + } + public String getSortingDisplayString() { return optionString("sorting", this.sorting.getName()); } @@ -95,10 +155,26 @@ public String getShowLibrariesDisplayString() { return optionString("show_libraries", I18n.getInstance().translateKey("option.modmenu.show_libraries." + this.showLibraries)); } + public String getHideConfigButtonsDisplayString() { + return optionString("hide_config_buttons", I18n.getInstance().translateKey("option.modmenu.hide_config_buttons." + this.hideConfigButtons)); + } + public String getHideBadgesDisplayString() { return optionString("hide_badges", I18n.getInstance().translateKey("option.modmenu.hide_badges." + this.hideBadges)); } + public String getHideModLinksDisplayString() { + return optionString("hide_mod_links", I18n.getInstance().translateKey("option.modmenu.hide_mod_links." + this.hideModLinks)); + } + + public String getHideModCreditsDisplayString() { + return optionString("hide_mod_credits", I18n.getInstance().translateKey("option.modmenu.hide_mod_credits." + this.hideModCredits)); + } + + public String getHideModLicenseDisplayString() { + return optionString("hide_mod_license", I18n.getInstance().translateKey("option.modmenu.hide_mod_license." + this.getHideModLicense())); + } + public String getEasterEggsDisplayString() { return optionString("easter_eggs", I18n.getInstance().translateKey("option.modmenu.easter_eggs." + this.easterEggs)); } @@ -111,6 +187,10 @@ public String getGameMenuButtonStyleDisplayString() { return optionString("game_menu_button_style", this.gameMenuButtonStyle.getName()); } + public String getRandomJavaColorsDisplayString() { + return optionString("random_java_colors", I18n.getInstance().translateKey("option.modmenu.random_java_colors." + this.randomJavaColors)); + } + public String getTranslateNamesDisplayString() { return optionString("translate_names", I18n.getInstance().translateKey("option.modmenu.translate_names." + this.translateNames)); } @@ -119,6 +199,10 @@ public String getTranslateDescriptionsDisplayString() { return optionString("translate_descriptions", I18n.getInstance().translateKey("option.modmenu.translate_descriptions." + this.translateDescriptions)); } + public String getQuickConfigureDisplayString() { + return optionString("quick_configure", I18n.getInstance().translateKey("option.modmenu.quick_configure." + this.quickConfigure)); + } + private static > T next(T current) { T[] values = current.getDeclaringClass().getEnumConstants(); return values[(current.ordinal() + 1) % values.length]; diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java index 77fad664d..ebe6f5544 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -70,7 +70,7 @@ public void init() { startY + 48, buttonWidth, buttonHeight, - config.getHideBadgesDisplayString() + config.getHideModLinksDisplayString() )); this.add(new ButtonElement( @@ -79,7 +79,7 @@ public void init() { startY + 48, buttonWidth, buttonHeight, - config.getEasterEggsDisplayString() + config.getHideModLicenseDisplayString() )); this.add(new ButtonElement( @@ -88,7 +88,7 @@ public void init() { startY + 72, buttonWidth, buttonHeight, - config.getTranslateNamesDisplayString() + config.getHideBadgesDisplayString() )); this.add(new ButtonElement( @@ -97,9 +97,64 @@ public void init() { startY + 72, buttonWidth, buttonHeight, + config.getHideModCreditsDisplayString() + )); + + this.add(new ButtonElement( + 109, + leftX, + startY + 96, + buttonWidth, + buttonHeight, + config.getEasterEggsDisplayString() + )); + + this.add(new ButtonElement( + 108, + rightX, + startY + 96, + buttonWidth, + buttonHeight, + config.getRandomJavaColorsDisplayString() + )); + + this.add(new ButtonElement( + 110, + leftX, + startY + 120, + buttonWidth, + buttonHeight, + config.getTranslateNamesDisplayString() + )); + + this.add(new ButtonElement( + 111, + rightX, + startY + 120, + buttonWidth, + buttonHeight, config.getTranslateDescriptionsDisplayString() )); + this.add(new ButtonElement( + 112, + leftX, + startY + 144, + buttonWidth, + buttonHeight, + config.getQuickConfigureDisplayString() + )); + + this.add(new ButtonElement( + 113, + rightX, + startY + 144, + buttonWidth, + buttonHeight, + config.getHideConfigButtonsDisplayString() + )); + + int doneButtonWidth = 200; this.add(new ButtonElement( 200, @@ -138,17 +193,35 @@ protected void buttonClicked(ButtonElement button) { config.toggleGameMenuButtonStyle(); button.displayString = config.getGameMenuButtonStyleDisplayString(); } else if (button.id == 104) { + config.toggleHideModLinks(); + button.displayString = config.getHideModLinksDisplayString(); + } else if (button.id == 105) { + config.toggleHideModLicense(); + button.displayString = config.getHideModLicenseDisplayString(); + } else if (button.id == 106) { config.toggleHideBadges(); button.displayString = config.getHideBadgesDisplayString(); - } else if (button.id == 105) { + } else if (button.id == 107) { + config.toggleHideModCredits(); + button.displayString = config.getHideModCreditsDisplayString(); + } else if (button.id == 108) { config.toggleEasterEggs(); button.displayString = config.getEasterEggsDisplayString(); - } else if (button.id == 106) { + } else if (button.id == 109) { + config.toggleRandomJavaColors(); + button.displayString = config.getRandomJavaColorsDisplayString(); + } else if (button.id == 110) { config.toggleTranslateNames(); button.displayString = config.getTranslateNamesDisplayString(); - } else if (button.id == 107) { + } else if (button.id == 111) { config.toggleTranslateDescriptions(); button.displayString = config.getTranslateDescriptionsDisplayString(); + } else if (button.id == 112) { + config.toggleQuickConfigure(); + button.displayString = config.getQuickConfigureDisplayString(); + } else if (button.id == 113) { + config.toggleHideConfigButtons(); + button.displayString = config.getHideConfigButtonsDisplayString(); } else if (button.id == 200) { this.closeAndReturn(); } diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index da7ffc523..49290ce5a 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -1,6 +1,7 @@ package io.github.prospector.modmenu.gui; +import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.RenderUtils; import net.fabricmc.loader.api.metadata.ContactInformation; @@ -21,6 +22,8 @@ public class DescriptionListWidget extends EntryListWidget= x && mouseY >= y && mouseX < x + rowWidth && mouseY < y + iconSize; + + if (hovered) { + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.COLOR); + GLRenderer.enableState(State.BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GLRenderer.setColor4f(1, 1, 1, 0.4f); + + TessellatorGeneral t = GLRenderer.getTessellator(); + t.startDrawingQuads(); + t.addVertexWithUV(x, y + iconSize, 0, 0, 0); + t.addVertexWithUV(x + iconSize, y + iconSize, 0, 1, 0); + t.addVertexWithUV(x + iconSize, y, 0, 1, 1); + t.addVertexWithUV(x, y, 0, 0, 1); + t.draw(); + GLRenderer.popFrame(); + + boolean hoveringIcon = mouseX < x + iconSize && mouseY < y + iconSize; + + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.INTERFACE); + GLRenderer.setColor4f(1, 1, 1, 1); + + client.textureManager.bindTexture(client.textureManager.loadTexture("/assets/modmenu/textures/gui/mod_configuration.png")); + + float vMin = hoveringIcon ? 32.0f / 256.0f : 0.0f; + float vMax = hoveringIcon ? 64.0f / 256.0f : 32.0f / 256.0f; + float uMax = 32.0f / 256.0f; + + t.startDrawingQuads(); + t.addVertexWithUV(x, y + iconSize, 0, 0.0f, vMax); + t.addVertexWithUV(x + iconSize, y + iconSize, 0, uMax, vMax); + t.addVertexWithUV(x + iconSize, y, 0, uMax, vMin); + t.addVertexWithUV(x, y, 0, 0.0f, vMin); + t.draw(); + GLRenderer.popFrame(); + + if (hoveringIcon) { + list.getParent().setDesiredCursor(net.minecraft.client.render.window.CursorShape.HAND); + } + } + } + String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; @@ -168,6 +226,21 @@ private BufferedImage createIcon() { @Override public void mouseClicked(int v, int v1, int i) { list.select(this); + + if (i == 0 && !(this instanceof ParentEntry)) { + String id = metadata.getId(); + if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id) && config.getQuickConfigure()) { + int iconLeft = getXOffset(); + if (v >= iconLeft && v < iconLeft + 32 && v1 >= 0 && v1 < 32) { + final net.minecraft.client.gui.Screen screen = ModMenu.getConfigScreen(id, list.getParent()); + if (screen != null) { + client.displayScreen(screen); + } else { + ModMenu.openConfigScreen(id); + } + } + } + } } public ModMetadata getMetadata() { diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index faa64991f..1b950f1e5 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -1,9 +1,11 @@ package io.github.prospector.modmenu.gui; import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.BadgeRenderer; import io.github.prospector.modmenu.util.ButtonUtil; +import io.github.prospector.modmenu.util.DrawingUtil; import io.github.prospector.modmenu.util.RenderUtils; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -54,6 +56,9 @@ public class ModListScreen extends Screen { public Set showModChildren = new HashSet<>(); private String lastSearchString = null; + ModMenuConfig config = ModMenuConfigManager.getConfig(); + I18n i18n = I18n.getInstance(); + private static final int CONFIGURE_BUTTON_ID = 0; private static final int WEBSITE_BUTTON_ID = 1; private static final int ISSUES_BUTTON_ID = 2; @@ -63,8 +68,6 @@ public class ModListScreen extends Screen { private static final int MODS_FOLDER_BUTTON_ID = 6; private static final int DONE_BUTTON_ID = 7; - I18n i18n = I18n.getInstance(); - public ModListScreen(Screen previousGui) { this.parent = previousGui; this.textTitle = i18n.translateKey("modmenu.title"); @@ -110,15 +113,14 @@ public void init() { this.descriptionListWidget = new DescriptionListWidget(this.mc, paneWidth, this.height, paneY + 60, this.height - 36, 9 + 1, this); this.descriptionListWidget.setLeftPos(rightPaneX); ButtonElement configureButton = new ModMenuTexturedButtonWidget(CONFIGURE_BUTTON_ID, width - 24, paneY, 20, 20, 0, 0, CONFIGURE_BUTTON_LOCATION, 32, 64) { - @Override public void render(Minecraft mc, int mouseX, int mouseY) { if (isHovered(mouseX, mouseY)) { setTooltip(i18n.translateKey("modmenu.configure")); } if (selected != null) { - String modid = selected.getMetadata().getId(); - enabled = ModMenu.hasConfigScreenFactory(modid) || ModMenu.hasLegacyConfigScreenTask(modid); + String id = selected.getMetadata().getId(); + enabled = (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id)); } else { enabled = false; } @@ -157,8 +159,8 @@ public void render(Minecraft mc, int int_1, int int_2) { } } }); - String showLibrariesText = ModMenuConfigManager.getConfig().getShowLibrariesDisplayString(); - String sortingText = ModMenuConfigManager.getConfig().getSortingDisplayString(); + String showLibrariesText = config.getShowLibrariesDisplayString(); + String sortingText = config.getSortingDisplayString(); int showLibrariesWidth = fontRenderer.stringWidth(showLibrariesText) + 20; int sortingWidth = fontRenderer.stringWidth(sortingText) + 20; int filtersX; @@ -174,7 +176,7 @@ public void render(Minecraft mc, int int_1, int int_2) { @Override public void drawButton(Minecraft mc, int mouseX, int mouseY) { visible = enabled = filterOptionsShown; - this.displayString = ModMenuConfigManager.getConfig().getSortingDisplayString(); + this.displayString = config.getSortingDisplayString(); super.drawButton(mc, mouseX, mouseY); } }); @@ -182,11 +184,13 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { @Override public void drawButton(Minecraft mc, int mouseX, int mouseY) { visible = enabled = filterOptionsShown; - this.displayString = ModMenuConfigManager.getConfig().getShowLibrariesDisplayString(); + this.displayString = config.getShowLibrariesDisplayString(); super.drawButton(mc, mouseX, mouseY); } }); - this.buttons.add(configureButton); + if (!config.getHideConfigButtons()) { + this.buttons.add(configureButton); + } this.buttons.add(websiteButton); this.buttons.add(issuesButton); this.buttons.add(ButtonUtil.createButton(MODS_FOLDER_BUTTON_ID, this.width / 2 - 154, this.height - 28, 150, 20, i18n.translateKey("modmenu.modsFolder"))); @@ -211,9 +215,8 @@ protected void buttonClicked(ButtonElement button) { } case WEBSITE_BUTTON_ID: { if (selected != null) { - String modId = selected.getMetadata().getId(); - - if ("minecraft".equals(modId)) { + String id = selected.getMetadata().getId(); + if ("minecraft".equals(id)) { UrlHelper.openURL("https://www.betterthanadventure.net/"); } else { selected.getMetadata().getContact().get("homepage").ifPresent(UrlHelper::openURL); @@ -223,9 +226,8 @@ protected void buttonClicked(ButtonElement button) { } case ISSUES_BUTTON_ID: { if (selected != null) { - String modId = selected.getMetadata().getId(); - - if ("minecraft".equals(modId)) { + String id = selected.getMetadata().getId(); + if ("minecraft".equals(id)) { UrlHelper.openURL("https://support.betterthanadventure.net/"); } else { selected.getMetadata().getContact().get("issues").ifPresent(UrlHelper::openURL); @@ -238,12 +240,12 @@ protected void buttonClicked(ButtonElement button) { break; } case TOGGLE_SORT_MODE_BUTTON_ID: { - ModMenuConfigManager.getConfig().toggleSortMode(); + config.toggleSortMode(); modList.reloadFilters(); break; } case TOGGLE_SHOW_LIBRARIES_BUTTON_ID: { - ModMenuConfigManager.getConfig().toggleShowLibraries(); + config.toggleShowLibraries(); modList.reloadFilters(); break; } @@ -341,20 +343,25 @@ public void render(int mouseX, int mouseY, float delta) { } if (selectedEntry != null) { ModMetadata metadata = selectedEntry.getMetadata(); + String id = metadata.getId(); + int x = rightPaneX; int prevColor = GLRenderer.getColor(); GLRenderer.setColor4f(1, 1, 1, 1); + if ("java".equals(id)) { + DrawingUtil.drawRandomVersionBackground(selectedEntry.container, x, paneY, 32, 32); + } + this.selected.bindIconTexture(); ModListEntry.internalRender(paneY, x); int lineSpacing = 9 + 1; int imageOffset = 36; - String id = metadata.getId(); String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; - if (!translatedName.equals(translationKey) && ModMenuConfigManager.getConfig().getTranslateNames()) { + if (!translatedName.equals(translationKey) && config.getTranslateNames()) { name = translatedName; } else { name = metadata.getName(); @@ -373,8 +380,8 @@ public void render(int mouseX, int mouseY, float delta) { } badgeRenderer.draw(mouseX, mouseY); String versionString = metadata.getId().equals("minecraft") - ? Global.VERSION - : metadata.getVersion().getFriendlyString(); + ? Global.VERSION + : metadata.getVersion().getFriendlyString(); this.drawStringNoShadow(font, "v" + versionString, x + imageOffset, paneY + 2 + lineSpacing, 0x808080); String authors; List names = new ArrayList<>(); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java index eeeee2339..7d8819409 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java @@ -2,6 +2,7 @@ import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.entries.ChildEntry; import io.github.prospector.modmenu.gui.entries.IndependentEntry; @@ -12,6 +13,7 @@ import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Screen; import net.minecraft.client.render.renderer.GLRenderer; import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.tessellator.TessellatorGeneral; @@ -35,6 +37,8 @@ public class ModListWidget extends AlwaysSelectedEntryListWidget i private boolean scrolling; private boolean isFocused; + ModMenuConfig config = ModMenuConfigManager.getConfig(); + public ModListWidget(Minecraft client, int width, int height, int y1, int y2, int entryHeight, String searchTerm, ModListWidget list, ModListScreen parent) { super(client, width, height, y1, y2, entryHeight); this.parent = parent; @@ -49,7 +53,7 @@ public ModListWidget(Minecraft client, int width, int height, int y1, int y2, in public void setScrollAmount(double amount) { super.setScrollAmount(amount); int denominator = Math.max(0, this.getMaxPosition() - (this.bottom - this.top - 4)); - if (denominator <= 0) { + if (denominator == 0) { parent.updateScrollPercent(0); } else { parent.updateScrollPercent(getScrollAmount() / Math.max(0, this.getMaxPosition() - (this.bottom - this.top - 4))); @@ -125,7 +129,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { if (this.modContainerList == null || refresh) { this.modContainerList = new ArrayList<>(); modContainerList.addAll(mods); - this.modContainerList.sort(ModMenuConfigManager.getConfig().getSorting().getComparator()); + this.modContainerList.sort(config.getSorting().getComparator()); } boolean validSearch = ModListSearch.validSearchQuery(searchTerm); @@ -137,7 +141,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { boolean library = ModMenu.LIBRARY_MODS.contains(modId); //Hide parent lib mods when the config is set to hide - if (library && !ModMenuConfigManager.getConfig().showLibraries()) { + if (library && !config.getShowLibraries()) { continue; } @@ -145,7 +149,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { if (ModMenu.PARENT_MAP.keySet().contains(container)) { //Add parent mods when not searching List children = ModMenu.PARENT_MAP.get(container); - children.sort(ModMenuConfigManager.getConfig().getSorting().getComparator()); + children.sort(config.getSorting().getComparator()); ParentEntry parent = new ParentEntry(minecraft, container, children, this); this.addEntry(parent); //Add children if they are meant to be shown @@ -239,6 +243,28 @@ public void mouseClicked(int double_1, int double_2, int int_1) { if (this.isMouseOver(double_1, double_2)) { ModListEntry entry = this.getEntryAtPos(double_1, double_2); if (entry != null) { + if (int_1 == 0 && !(entry instanceof ParentEntry) && config.getQuickConfigure()) { + String id = entry.getMetadata().getId(); + if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id)) { + int iconLeft = getRowLeft() + entry.getXOffset(); + if (double_1 >= iconLeft && double_1 < iconLeft + 32) { + int index = children().indexOf(entry); + if (index >= 0) { + int entryTop = this.getRowTop(index) + 2; + if (double_2 >= entryTop && double_2 < entryTop + 32) { + final Screen screen = ModMenu.getConfigScreen(id, parent); + if (screen != null) { + minecraft.displayScreen(screen); + } else { + ModMenu.openConfigScreen(id); + } + return; + } + } + } + } + } + if (entry.list.getFocused() != null) { if (!entry.list.getFocused().equals(entry)) { this.setFocused(entry); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java index 5ad969541..cbc426688 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java @@ -24,10 +24,11 @@ public class MixinGuiMainMenu extends Screen { public void modmenu$drawMenuButton(CallbackInfo info) { ButtonElement texturePackButton = this.buttons.get(2); I18n i18n = I18n.getInstance(); - if (ModMenuConfigManager.getConfig().getEasterEggs()) { - texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); + ModMenuConfig config = ModMenuConfigManager.getConfig(); + if (config.getEasterEggs()) { + texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); } - if (ModMenuConfigManager.getConfig().getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { + if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { int newWidth = ((GuiButtonAccessor) texturePackButton).getWidth() / 2 - 1; ((GuiButtonAccessor) texturePackButton).setWidth(newWidth); String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); diff --git a/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java b/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java new file mode 100644 index 000000000..9914b9102 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java @@ -0,0 +1,51 @@ +package io.github.prospector.modmenu.util; + +import io.github.prospector.modmenu.config.ModMenuConfigManager; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.metadata.ModMetadata; +import net.minecraft.client.render.renderer.GLRenderer; +import net.minecraft.client.render.renderer.Shaders; +import net.minecraft.client.render.renderer.State; +import net.minecraft.client.render.tessellator.TessellatorGeneral; +import org.lwjgl.opengl.GL11; + +import java.awt.Color; +import java.util.Random; + +@Environment(EnvType.CLIENT) +public class DrawingUtil { + public static void drawRandomVersionBackground(ModContainer container, int x, int y, int width, int height) { + ModMetadata meta = container.getMetadata(); + int seed = meta.getName().hashCode() + meta.getVersion().getFriendlyString().hashCode(); + + Random random = new Random(seed); + int color = 0xFF000000 | Color.HSBtoRGB(random.nextFloat(), random.nextFloat() * 0.1f + 0.7f, 0.9f); + if (!ModMenuConfigManager.getConfig().getRandomJavaColors()) { + color = 0xFFDD5656; + } + + float red = (float) ((color >> 16) & 255) / 255.0f; + float green = (float) ((color >> 8) & 255) / 255.0f; + float blue = (float) (color & 255) / 255.0f; + float alpha = (float) ((color >> 24) & 255) / 255.0f; + + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.COLOR); + GLRenderer.enableState(State.BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + GLRenderer.setColor4f(red, green, blue, alpha); + + TessellatorGeneral t = GLRenderer.getTessellator(); + t.startDrawingQuads(); + t.addVertexWithUV(x, y, 0, 0, 0); + t.addVertexWithUV(x, y + height, 0, 0, 1); + t.addVertexWithUV(x + width, y + height, 0, 1, 1); + t.addVertexWithUV(x + width, y, 0, 1, 0); + t.draw(); + + GLRenderer.popFrame(); + } +} \ No newline at end of file diff --git a/src/main/resources/assets/modmenu/lang/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang index a80a3d42c..82e779c3e 100644 --- a/src/main/resources/assets/modmenu/lang/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US.lang @@ -20,24 +20,42 @@ option.modmenu.sorting.descending=Z-A option.modmenu.show_libraries=Libraries option.modmenu.show_libraries.true=Shown option.modmenu.show_libraries.false=Hidden -option.modmenu.game_menu_button_style=Game Menu -option.modmenu.game_menu_button_style.insert=Insert -option.modmenu.game_menu_button_style.icon=Icon -option.modmenu.mods_button_style=Title Screen -option.modmenu.mods_button_style.insert=Insert -option.modmenu.mods_button_style.icon=Icon +option.modmenu.hide_config_buttons=Config Buttons +option.modmenu.hide_config_buttons.true=Hidden +option.modmenu.hide_config_buttons.false=Shown option.modmenu.hide_badges=Mod Badges option.modmenu.hide_badges.true=Hidden option.modmenu.hide_badges.false=Shown +option.modmenu.hide_mod_links=Mod Links +option.modmenu.hide_mod_links.true=Hidden +option.modmenu.hide_mod_links.false=Shown +option.modmenu.hide_mod_credits=Mod Credits +option.modmenu.hide_mod_credits.true=Hidden +option.modmenu.hide_mod_credits.false=Shown +option.modmenu.hide_mod_license=Mod License +option.modmenu.hide_mod_license.true=Hidden +option.modmenu.hide_mod_license.false=Shown option.modmenu.easter_eggs=Easter Eggs option.modmenu.easter_eggs.true=Enabled option.modmenu.easter_eggs.false=Disabled +option.modmenu.mods_button_style=Title Screen +option.modmenu.mods_button_style.insert=Insert +option.modmenu.mods_button_style.icon=Icon +option.modmenu.game_menu_button_style=Game Menu +option.modmenu.game_menu_button_style.insert=Insert +option.modmenu.game_menu_button_style.icon=Icon +option.modmenu.random_java_colors=Java Color +option.modmenu.random_java_colors.true=Vendor +option.modmenu.random_java_colors.false=Always Red option.modmenu.translate_names=Names option.modmenu.translate_names.true=Localized option.modmenu.translate_names.false=Not Localized option.modmenu.translate_descriptions=Descriptions option.modmenu.translate_descriptions.true=Localized option.modmenu.translate_descriptions.false=Not Localized +option.modmenu.quick_configure=Quick Configure +option.modmenu.quick_configure.true=Enabled +option.modmenu.quick_configure.false=Disabled modmenu.credits=Credits: modmenu.authors=Authors: diff --git a/src/main/resources/assets/modmenu/textures/gui/mod_configuration.png b/src/main/resources/assets/modmenu/textures/gui/mod_configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1ca1a770798e8c8d740f793cb7f5dad36d41ca GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58CaNs)Vi3hp+HI^z$e5NNdN!;zhlzL?(S|A zyHay=a|U)hpkS47_9P(1SQ6wH%;50sMjDW_*3-o?q~g}wYk|DZjvTHBqYgOf+Hh|T z;Ep&@w>i=FLW?AG_4ns3*X>s%e{5LQRq-myQbFle=KZl*l0JZ#7^chhEf zSdFD@I*mma e&{^2P0Xuu+FBkQ9z7=o>Nqf5bxvXNklxZC5OA^#%rG-~zk1Z8XL;LnqXdEU#~h- zC=_Hbl}gE6EEY{NnXKZ|jm^zXv$nRTkCA5q05<|^jZ&#(P!5npB4K!DW;8c9o6E~f zb8~ZJPESv39S_~}03euKH#K=%qL^n@2oF{O5F?k%2{6`bDb{g@XhR@MxY5eWN~H6% zv$JM-dD%=(P6}|q)iVTu?OOzIZ*NDEH8C+^4i68_@bIvJk94SlNdVwOy@6U_dV1Qd zuC7+FS3gcoP03je+G>vA9SLjoyDemQcQ^RtEp=~iFEBg8gm@9BX@*bt_xI%-R)`8D zOpza501$v0#hS}AGc%C_5|CuW_4T!%j=+6!0siMt(~T25J3D~~m57A^-_p{eTd`)( z1tpYRM#=)z7CbsS+E@^7 zZEcz3<71Hti1zk&lTN4Qnd->xPEJnbE!f-JYwMQv(a}-WUepZ^HNM4oAa zYPE#WIGf34%+k~G*l4{p%x+|A9(Mg-a#AfRKolr}nNFw7n>W|ai>P3Zj{dEAK5Wx- z0g7@tw-eSw#bU|nbW)<6J(L548QRs=jZ?f*0;JwzbXx~7Gvzd879NkO{=j+hyR4c2 z{Gs{stCx~>pLOZZPee#&+%Q%EV!<=mE>(*)B;_wRdY^`R%kD(`bh z5cc=?iw=COzj8FH{rpbph6bOu=6o-TI|P6#(?~|R+aI{^2=_hVb#I~rr37Z)X(sI7 z6>$NGR8?qyBSMf`L~qERg5?bS&;YLB;fXVi%-Pvl7`rcB0O$=cFQ45QgM))6iZuoX1{$(JS!CHU(AnAP zi`pi1b#+M`*l}Xh!`5XMKnOnV?(UXzSRpEqFhzcJ0brxoT#o7j_(;WiM_C$biXklO z&Eew$_9fzg5eukdg3&g+y$%wpUsHm3{AdEeCTirzae-gCw&nV*#RXVEt)Pr?Tu|%0 z$_bmxT!0^Kv)dUFB&c5hBU1p_CR7*L3IW9}^)r4Yt={(la-=1rnlTiJ)&beP{dazT zE~{FuxwyCx+Aa84EtyzAOEELIL7>Kjf>JOF1YT(HHR6J@I)EUAQdlU Date: Sat, 10 Oct 2026 18:09:52 +0300 Subject: [PATCH 45/59] Update ModMenuConfigScreen.java --- .../prospector/modmenu/config/ModMenuConfigScreen.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java index ebe6f5544..2d93c45c0 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -16,7 +16,6 @@ public ModMenuConfigScreen(Screen parent) { @Override public void init() { - I18n i18n = I18n.getInstance(); ModMenuConfig config = ModMenuConfigManager.getConfig(); int buttonWidth = 150; @@ -162,7 +161,7 @@ public void init() { this.height - 28, doneButtonWidth, buttonHeight, - i18n.translateKey("gui.options.button.done") + I18n.getInstance().translateKey("gui.options.button.done") )); } @@ -170,8 +169,7 @@ public void init() { public void render(int mouseX, int mouseY, float delta) { this.renderBackground(); - I18n i18n = I18n.getInstance(); - this.drawStringCenteredNoShadow(this.fontRenderer, i18n.translateKey("modmenu.options"), this.width / 2, 20, 0xFFFFFF); + this.drawStringCenteredNoShadow(this.fontRenderer, I18n.getInstance().translateKey("modmenu.options"), this.width / 2, 20, 0xFFFFFF); super.render(mouseX, mouseY, delta); } From 1474f0895c52911614eb685d18acd465bd134cf0 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:01:21 +0300 Subject: [PATCH 46/59] I don't know why, but is better --- .../resources/assets/modmenu/unknown_icon.png | Bin 98 -> 150 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/modmenu/unknown_icon.png b/src/main/resources/assets/modmenu/unknown_icon.png index 9d2cddd033fcf214de69c46ace99024345c6a2b0..e1f16e09981567b7b4cde84867192c88b624fda0 100644 GIT binary patch delta 131 zcmV-}0DS*qmI06%eFgvk03Y-JVE_OCT}ebiR5%f1jEIQ%&p-iSqChWLD+{^-%a<=l z7ee7jMn1oMU-q3P>kY-O;KbLh*2~7Yh9u%1X From 961d68bcd5644ca44392ecfa41a18b3b78f98f33 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:13:43 +0300 Subject: [PATCH 47/59] old smallest not commit --- .../io/github/prospector/modmenu/ModMenu.java | 2 -- .../prospector/modmenu/api/ModMenuApi.java | 1 - .../modmenu/config/ModMenuConfigManager.java | 1 - .../gui/AlwaysSelectedEntryListWidget.java | 1 - .../modmenu/gui/DescriptionListWidget.java | 1 - .../modmenu/gui/EntryListWidget.java | 18 ++++-------------- .../prospector/modmenu/gui/ModListEntry.java | 17 +++++++---------- .../prospector/modmenu/gui/ModListWidget.java | 18 ------------------ .../modmenu/gui/ModMenuButtonWidget.java | 1 - .../gui/ModMenuTexturedButtonWidget.java | 3 +-- .../modmenu/gui/TextFieldWidget.java | 1 - .../modmenu/gui/entries/ChildEntry.java | 1 - .../modmenu/gui/entries/IndependentEntry.java | 1 - .../modmenu/gui/entries/ParentEntry.java | 1 - ...ccessor.java => ButtonElementAccessor.java} | 3 +-- .../mixin/{MixinI18n.java => I18nMixin.java} | 7 ++----- .../modmenu/mixin/LanguageAccessor.java | 5 +---- .../modmenu/mixin/MinecraftAccessor.java | 1 - ...iMainMenu.java => ScreenMainMenuMixin.java} | 7 +++---- ...uiIngameMenu.java => ScreenPauseMixin.java} | 3 +-- .../modmenu/mixin/TextFieldEditorAccessor.java | 1 - ...TexturePacks.java => TexturePackMixin.java} | 5 ++--- .../prospector/modmenu/util/BadgeRenderer.java | 7 ++++--- .../prospector/modmenu/util/ButtonUtil.java | 5 ++--- .../prospector/modmenu/util/ModListSearch.java | 1 - .../prospector/modmenu/util/RenderUtils.java | 1 - .../modmenu/util/TestModContainer.java | 3 +-- src/main/resources/modmenu.mixins.json | 10 +++++----- 28 files changed, 34 insertions(+), 92 deletions(-) rename src/main/java/io/github/prospector/modmenu/mixin/{GuiButtonAccessor.java => ButtonElementAccessor.java} (89%) rename src/main/java/io/github/prospector/modmenu/mixin/{MixinI18n.java => I18nMixin.java} (94%) rename src/main/java/io/github/prospector/modmenu/mixin/{MixinGuiMainMenu.java => ScreenMainMenuMixin.java} (91%) rename src/main/java/io/github/prospector/modmenu/mixin/{MixinGuiIngameMenu.java => ScreenPauseMixin.java} (97%) rename src/main/java/io/github/prospector/modmenu/mixin/{MixinTexturePacks.java => TexturePackMixin.java} (90%) diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 8d2e42bfb..63a7ac787 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu; - import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -131,7 +130,6 @@ public void onInitialize() { // Hardcode marking as library if (id.equals("fabricloader") - || id.equals("fabric") || id.equals("java") || id.equals("mixinextras")) { addLibraryMod(id); diff --git a/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java b/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java index c9cc10f99..3e86572e6 100644 --- a/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java +++ b/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.api; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.util.TriConsumer; import net.minecraft.client.gui.Screen; diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java index ea589446b..dcff5fcd7 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.config; - import io.github.prospector.modmenu.ModMenu; import net.fabricmc.loader.api.FabricLoader; diff --git a/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java index 447b6a2f5..a636855a1 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 49290ce5a..d20343982 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.RenderUtils; diff --git a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java index 09fc45c0f..049a42535 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; @@ -352,30 +351,22 @@ public boolean mouseReleased(double d, double e, int i) { return false; } - public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double mouseDX, double mouseDY) { + public void mouseDragged(double mouseX, double mouseY, int mouseButton, double mouseDX, double mouseDY) { if (this.scrolling) { setScrollAmount(getScrollAmount() - mouseDY); - return true; } - return false; } - public boolean mouseScrolled(double d, double e, double f) { + public void mouseScrolled(double d, double e, double f) { this.setScrollAmount(this.getScrollAmount() - f * (double)this.itemHeight / 2.0D); - return true; } - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + public void keyPressed(int keyCode, int scanCode, int modifiers) { if (this.getFocused() != null && this.getFocused().keyPressed(keyCode, scanCode, modifiers)) { - return true; } else if (keyCode == 264) { this.moveSelection(1); - return true; } else if (keyCode == 265) { this.moveSelection(-1); - return true; - } else { - return false; } } @@ -552,8 +543,7 @@ public void mouseMoved(double d, double e) { public void mouseClicked(int d, int e, int f) { } - public boolean mouseReleased(double d, double e, int i) { - return false; + public void mouseReleased(double d, double e, int i) { } public boolean mouseScrolled(double d, double e, double f) { diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 294c0e4b9..8314ad97b 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; @@ -12,11 +11,13 @@ import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Screen; import net.minecraft.client.render.font.FontRenderer; import net.minecraft.client.render.renderer.GLRenderer; import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.renderer.State; import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.client.render.window.CursorShape; import net.minecraft.core.lang.I18n; import org.lwjgl.opengl.GL11; import org.slf4j.Logger; @@ -30,7 +31,7 @@ import java.util.Objects; public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry { - public static final String UNKNOWN_ICON = "/gui/unknown_pack.png"; + public static final String UNKNOWN_ICON = "/assets/modmenu/unknown_icon.png"; private static final Logger LOGGER = LoggerFactory.getLogger(ModMenu.MOD_ID); protected final Minecraft client; @@ -38,8 +39,7 @@ public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry= x && mouseY >= y && mouseX < x + rowWidth && mouseY < y + iconSize; @@ -112,7 +109,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou GLRenderer.popFrame(); if (hoveringIcon) { - list.getParent().setDesiredCursor(net.minecraft.client.render.window.CursorShape.HAND); + list.getParent().setDesiredCursor(CursorShape.HAND); } } } @@ -154,7 +151,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou int maxLines = 2; int currentLine = 0; - font = this.fontRenderer; + String[] paragraphs = description.split("\r?\n"); for (String paragraph : paragraphs) { @@ -232,7 +229,7 @@ public void mouseClicked(int v, int v1, int i) { if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id) && config.getQuickConfigure()) { int iconLeft = getXOffset(); if (v >= iconLeft && v < iconLeft + 32 && v1 >= 0 && v1 < 32) { - final net.minecraft.client.gui.Screen screen = ModMenu.getConfigScreen(id, list.getParent()); + final Screen screen = ModMenu.getConfigScreen(id, list.getParent()); if (screen != null) { client.displayScreen(screen); } else { diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java index 7d8819409..818d8b8a1 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; @@ -18,15 +17,12 @@ import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.tessellator.TessellatorGeneral; import net.minecraft.core.util.helper.MathHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.awt.image.BufferedImage; import java.nio.file.Path; import java.util.*; public class ModListWidget extends AlwaysSelectedEntryListWidget implements AutoCloseable { - private static final Logger LOGGER = LoggerFactory.getLogger(ModMenu.MOD_ID); public static final boolean DEBUG = Boolean.getBoolean("modmenu.debug"); private final Map modIconsCache = new HashMap<>(); @@ -34,8 +30,6 @@ public class ModListWidget extends AlwaysSelectedEntryListWidget i private List modContainerList = null; private Set addedMods = new HashSet<>(); private String selectedModId = null; - private boolean scrolling; - private boolean isFocused; ModMenuConfig config = ModMenuConfigManager.getConfig(); @@ -60,11 +54,6 @@ public void setScrollAmount(double amount) { } } - @Override - protected boolean isFocused() { - return isFocused; - } - public void select(ModListEntry entry) { this.setSelected(entry); } @@ -132,7 +121,6 @@ private void filter(String searchTerm, boolean refresh, boolean search) { this.modContainerList.sort(config.getSorting().getComparator()); } - boolean validSearch = ModListSearch.validSearchQuery(searchTerm); List matched = ModListSearch.search(parent, searchTerm, modContainerList); for (ModContainer container : matched) { @@ -231,12 +219,6 @@ protected void renderList(int x, int y, int mouseX, int mouseY, float delta) { } - @Override - protected void updateScrollingState(double double_1, double double_2, int int_1) { - super.updateScrollingState(double_1, double_2, int_1); - this.scrolling = int_1 == 0 && double_1 >= (double) this.getScrollbarPosition() && double_1 < (double) (this.getScrollbarPosition() + 6); - } - @Override public void mouseClicked(int double_1, int double_2, int int_1) { this.updateScrollingState(double_1, double_2, int_1); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java index 29c7a7234..8495e8680 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import net.minecraft.client.gui.ButtonElement; public class ModMenuButtonWidget extends ButtonElement { diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java index 9f164fa39..55398e857 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.render.font.FontRenderer; @@ -55,7 +54,7 @@ public void render(Minecraft mc, int mouseX, int mouseY) { if (hovered && mc.currentScreen != null && this.enabled) { mc.currentScreen.setDesiredCursor(CursorShape.HAND); } - + int adjustedV = this.v; if (!enabled) { adjustedV += this.height * 2; diff --git a/src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java b/src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java index 0dd512c13..25b97491c 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui; - import io.github.prospector.modmenu.mixin.MinecraftAccessor; import io.github.prospector.modmenu.mixin.TextFieldEditorAccessor; import net.minecraft.client.gui.Screen; diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java b/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java index 65831e077..91017df62 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui.entries; - import io.github.prospector.modmenu.gui.ModListEntry; import io.github.prospector.modmenu.gui.ModListWidget; import net.fabricmc.loader.api.ModContainer; diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java b/src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java index cecc1aaa8..3e2328758 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui.entries; - import io.github.prospector.modmenu.gui.ModListEntry; import io.github.prospector.modmenu.gui.ModListWidget; import net.fabricmc.loader.api.ModContainer; diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java b/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java index bcfda4212..d7bd36f6d 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.gui.entries; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.gui.ModListEntry; import io.github.prospector.modmenu.gui.ModListWidget; diff --git a/src/main/java/io/github/prospector/modmenu/mixin/GuiButtonAccessor.java b/src/main/java/io/github/prospector/modmenu/mixin/ButtonElementAccessor.java similarity index 89% rename from src/main/java/io/github/prospector/modmenu/mixin/GuiButtonAccessor.java rename to src/main/java/io/github/prospector/modmenu/mixin/ButtonElementAccessor.java index f7fb5fe43..37f2f1980 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/GuiButtonAccessor.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ButtonElementAccessor.java @@ -1,12 +1,11 @@ package io.github.prospector.modmenu.mixin; - import net.minecraft.client.gui.ButtonElement; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(value = ButtonElement.class, remap = false) -public interface GuiButtonAccessor { +public interface ButtonElementAccessor { @Accessor int getWidth(); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java b/src/main/java/io/github/prospector/modmenu/mixin/I18nMixin.java similarity index 94% rename from src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java rename to src/main/java/io/github/prospector/modmenu/mixin/I18nMixin.java index f7029389e..1734e2330 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinI18n.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/I18nMixin.java @@ -16,7 +16,7 @@ import java.util.Properties; @Mixin(value = I18n.class, remap = false) -public class MixinI18n { +public class I18nMixin { @Shadow private Language currentLanguage; @Shadow @@ -24,10 +24,7 @@ public static InputStream getResourceAsStream(String path) { throw new AssertionError(); } - @Inject( - method = "reload", - at = @At("TAIL") - ) + @Inject(method = "reload", at = @At("TAIL")) private void modmenu$addLangEntries(String languageCode, CallbackInfo ci) { Properties entries = ((LanguageAccessor) currentLanguage).getEntries(); String lang = "/assets/modmenu/lang/" + currentLanguage.getId() + ".lang"; diff --git a/src/main/java/io/github/prospector/modmenu/mixin/LanguageAccessor.java b/src/main/java/io/github/prospector/modmenu/mixin/LanguageAccessor.java index 6ea8a752f..83356ebba 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/LanguageAccessor.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/LanguageAccessor.java @@ -6,10 +6,7 @@ import java.util.Properties; -@Mixin( - value = Language.class, - remap = false -) +@Mixin(value = Language.class, remap = false) public interface LanguageAccessor { @Accessor Properties getEntries(); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MinecraftAccessor.java b/src/main/java/io/github/prospector/modmenu/mixin/MinecraftAccessor.java index fd5e6eb41..3dedb1ac1 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MinecraftAccessor.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/MinecraftAccessor.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.mixin; - import net.minecraft.client.Minecraft; import net.minecraft.core.enums.EnumOS; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java similarity index 91% rename from src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java rename to src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java index cbc426688..c4ffb639b 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiMainMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.mixin; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; @@ -19,7 +18,7 @@ import java.util.Random; @Mixin(value = ScreenMainMenu.class, remap = false) -public class MixinGuiMainMenu extends Screen { +public class ScreenMainMenuMixin extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { ButtonElement texturePackButton = this.buttons.get(2); @@ -29,8 +28,8 @@ public class MixinGuiMainMenu extends Screen { texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); } if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { - int newWidth = ((GuiButtonAccessor) texturePackButton).getWidth() / 2 - 1; - ((GuiButtonAccessor) texturePackButton).setWidth(newWidth); + int newWidth = ((ButtonElementAccessor) texturePackButton).getWidth() / 2 - 1; + ((ButtonElementAccessor) texturePackButton).setWidth(newWidth); String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); this.buttons.add(new ModMenuButtonWidget(100, this.width / 2 + 2, texturePackButton.yPosition, newWidth, 20, buttonText)); } else { diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java similarity index 97% rename from src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java rename to src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java index 66d4acd48..ddbc9da0b 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinGuiIngameMenu.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.mixin; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; @@ -17,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = ScreenPause.class, remap = false) -public class MixinGuiIngameMenu extends Screen { +public class ScreenPauseMixin extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { if (ModMenuConfigManager.getConfig().getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { diff --git a/src/main/java/io/github/prospector/modmenu/mixin/TextFieldEditorAccessor.java b/src/main/java/io/github/prospector/modmenu/mixin/TextFieldEditorAccessor.java index ae9815373..501fc5fc3 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/TextFieldEditorAccessor.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/TextFieldEditorAccessor.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.mixin; - import net.minecraft.client.gui.text.TextFieldEditor; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/main/java/io/github/prospector/modmenu/mixin/MixinTexturePacks.java b/src/main/java/io/github/prospector/modmenu/mixin/TexturePackMixin.java similarity index 90% rename from src/main/java/io/github/prospector/modmenu/mixin/MixinTexturePacks.java rename to src/main/java/io/github/prospector/modmenu/mixin/TexturePackMixin.java index 5ad18fdd3..59df01fb0 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/MixinTexturePacks.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/TexturePackMixin.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.mixin; - import io.github.prospector.modmenu.ModMenu; import net.minecraft.client.render.texturepack.TexturePack; import org.spongepowered.asm.mixin.Mixin; @@ -10,8 +9,8 @@ import java.io.InputStream; -@Mixin(value = {TexturePack.class}, remap = false) -public class MixinTexturePacks { +@Mixin(value = TexturePack.class, remap = false) +public class TexturePackMixin { @Inject(method = "getResourceAsStream", at = @At(value = "INVOKE", target = "Ljava/lang/Class;getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;", remap = false), cancellable = true) private void modmenu$onGetResource(String resource, CallbackInfoReturnable ci) { InputStream in = ModMenu.class.getClassLoader().getResourceAsStream(resource); diff --git a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java index 52c3d056d..c2d8ed04f 100644 --- a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java +++ b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.util; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; @@ -20,6 +19,8 @@ public class BadgeRenderer { protected Minecraft client; protected final ModListScreen screen; + ModMenuConfig config = ModMenuConfigManager.getConfig(); + public BadgeRenderer(Minecraft client, int startX, int startY, int endX, ModContainer container, ModListScreen screen) { this.startX = startX; this.startY = startY; @@ -55,7 +56,7 @@ public void draw(int mouseX, int mouseY) { drawBadge(entry.getKey(), entry.getValue().getKey(), entry.getValue().getValue(), mouseX, mouseY); } } - if (ModMenuConfigManager.getConfig().getEasterEggs()) { + if (config.getEasterEggs()) { //noinspection MagicConstant if (Calendar.getInstance().get(0b10) == 0b11 && Calendar.getInstance().get(0b101) == 0x1) { if (metadata.getId().equals(new String(new byte[]{109, 111, 100, 109, 101, 110, 117}))) { @@ -70,7 +71,7 @@ public void draw(int mouseX, int mouseY) { } public void drawBadge(String text, int outlineColor, int fillColor, int mouseX, int mouseY) { - if (!ModMenuConfigManager.getConfig().getHideBadges()) { + if (!config.getHideBadges()) { int width = client.font.stringWidth(text) + 6; if (badgeX + width < badgeMax) { RenderUtils.INSTANCE.drawBadge(screen, badgeX, badgeY, width, text, outlineColor, fillColor, 0xCACACA); diff --git a/src/main/java/io/github/prospector/modmenu/util/ButtonUtil.java b/src/main/java/io/github/prospector/modmenu/util/ButtonUtil.java index 29ba70825..b0b26ef9e 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ButtonUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/ButtonUtil.java @@ -1,13 +1,12 @@ package io.github.prospector.modmenu.util; - -import io.github.prospector.modmenu.mixin.GuiButtonAccessor; +import io.github.prospector.modmenu.mixin.ButtonElementAccessor; import net.minecraft.client.gui.ButtonElement; public final class ButtonUtil { public static ButtonElement createButton(int buttonId, int x, int y, int width, int height, String text) { ButtonElement button = new ButtonElement(buttonId, x, y, text); - GuiButtonAccessor accessor = (GuiButtonAccessor) button; + ButtonElementAccessor accessor = (ButtonElementAccessor) button; accessor.setWidth(width); accessor.setHeight(height); return button; diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index 1ecf7df52..21fb4d2df 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.util; - import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; diff --git a/src/main/java/io/github/prospector/modmenu/util/RenderUtils.java b/src/main/java/io/github/prospector/modmenu/util/RenderUtils.java index 1db829870..89394cc94 100644 --- a/src/main/java/io/github/prospector/modmenu/util/RenderUtils.java +++ b/src/main/java/io/github/prospector/modmenu/util/RenderUtils.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.util; - import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.Screen; diff --git a/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java b/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java index a67f03748..cbdfccbb1 100644 --- a/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java +++ b/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java @@ -1,6 +1,5 @@ package io.github.prospector.modmenu.util; - import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.SemanticVersion; @@ -214,7 +213,7 @@ public Map getCustomValues() { return new HashMap<>(); } - @Override + @Override public boolean containsCustomElement(String key) { return false; } diff --git a/src/main/resources/modmenu.mixins.json b/src/main/resources/modmenu.mixins.json index bbd0bd053..53a476703 100644 --- a/src/main/resources/modmenu.mixins.json +++ b/src/main/resources/modmenu.mixins.json @@ -4,14 +4,14 @@ "compatibilityLevel": "JAVA_${java}", "plugin": "io.github.prospector.modmenu.mixin.ModMenuMixinConfigPlugin", "client": [ - "MixinGuiIngameMenu", - "MixinGuiMainMenu", - "MixinTexturePacks", - "GuiButtonAccessor", + "ScreenPauseMixin", + "ScreenMainMenuMixin", + "TexturePackMixin", + "ButtonElementAccessor", "MinecraftAccessor", "LanguageAccessor", "TextFieldEditorAccessor", - "MixinI18n" + "I18nMixin" ], "injectors": { "defaultRequire": 1 From 493cbd064d6e0d6e51511597a4423cd9eb037651 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:50:22 +0300 Subject: [PATCH 48/59] show mods and libs --- .../io/github/prospector/modmenu/ModMenu.java | 7 +- .../modmenu/gui/EntryListWidget.java | 1 - .../prospector/modmenu/gui/ModListScreen.java | 73 ++++++++-- .../prospector/modmenu/gui/ModListWidget.java | 14 +- .../modmenu/util/TranslationUtil.java | 63 +++++++++ .../resources/assets/modmenu/lang/en_US.lang | 130 ++++++++++-------- 6 files changed, 206 insertions(+), 82 deletions(-) create mode 100644 src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 63a7ac787..3eabe099f 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -71,7 +71,8 @@ public static void addLibraryMod(String modid) { @Override public void onInitialize() { ModMenuConfigManager.initializeConfig(); - FabricLoader.getInstance().getEntrypointContainers("modmenu", ModMenuApi.class).forEach(entrypoint -> { + FabricLoader loader = FabricLoader.getInstance(); + loader.getEntrypointContainers("modmenu", ModMenuApi.class).forEach(entrypoint -> { ModMenuApi api = entrypoint.getEntrypoint(); ModContainer mod = entrypoint.getProvider(); try { @@ -85,7 +86,7 @@ public void onInitialize() { }); }); configScreenFactories.put("minecraft", (screenBase -> new ScreenOptions(screenBase, OptionsPages.GENERAL))); - Collection mods = FabricLoader.getInstance().getAllMods(); + Collection mods = loader.getAllMods(); for (ModContainer mod : mods) { ModMetadata metadata = mod.getMetadata(); String id = metadata.getId(); @@ -120,7 +121,7 @@ public void onInitialize() { if (metadata.containsCustomValue("modmenu:parent")) { String parentId = metadata.getCustomValue("modmenu:parent").getAsString(); if (parentId != null) { - Optional parent = FabricLoader.getInstance().getModContainer(parentId); + Optional parent = loader.getModContainer(parentId); parent.ifPresent(modContainer -> { final List list = PARENT_MAP.computeIfAbsent(modContainer, k -> new ArrayList<>()); list.add(mod); diff --git a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java index 049a42535..d40cd7c43 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java @@ -159,7 +159,6 @@ protected void clickedHeader(int i, int j) { protected void renderHeader(int i, int j) { } - public void renderBackground() { } diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 1b950f1e5..e9a29ef2b 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -3,10 +3,7 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.util.BadgeRenderer; -import io.github.prospector.modmenu.util.ButtonUtil; -import io.github.prospector.modmenu.util.DrawingUtil; -import io.github.prospector.modmenu.util.RenderUtils; +import io.github.prospector.modmenu.util.*; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.Person; @@ -32,6 +29,7 @@ import java.io.File; import java.text.NumberFormat; import java.util.*; +import java.util.stream.Collectors; public class ModListScreen extends Screen { private static final String FILTERS_BUTTON_LOCATION = "/assets/" + ModMenu.MOD_ID + "/textures/gui/filters_button.png"; @@ -58,6 +56,7 @@ public class ModListScreen extends Screen { ModMenuConfig config = ModMenuConfigManager.getConfig(); I18n i18n = I18n.getInstance(); + FabricLoader loader = FabricLoader.getInstance(); private static final int CONFIGURE_BUTTON_ID = 0; private static final int WEBSITE_BUTTON_ID = 1; @@ -98,7 +97,6 @@ public void tick() { @Override public void init() { Keyboard.enableRepeatEvents(true); - FontRenderer font = this.fontRenderer; paneY = 48; paneWidth = this.width / 2 - 8; rightPaneX = width - paneWidth; @@ -165,7 +163,7 @@ public void render(Minecraft mc, int int_1, int int_2) { int sortingWidth = fontRenderer.stringWidth(sortingText) + 20; int filtersX; int filtersWidth = showLibrariesWidth + sortingWidth + 2; - if ((filtersWidth + fontRenderer.stringWidth(i18n.translateKeyAndFormat("modmenu.showingMods", NumberFormat.getInstance().format(modList.getDisplayedCount()) + "/" + NumberFormat.getInstance().format(FabricLoader.getInstance().getAllMods().size()))) + 20) >= searchBoxX + searchBoxWidth + 22) { + if ((filtersWidth + fontRenderer.stringWidth(i18n.translateKeyAndFormat("modmenu.showingMods", NumberFormat.getInstance().format(modList.getDisplayedCount()) + "/" + NumberFormat.getInstance().format(loader.getAllMods().size()))) + 20) >= searchBoxX + searchBoxWidth + 22) { filtersX = paneWidth / 2 - filtersWidth / 2; showModCount = false; } else { @@ -250,7 +248,7 @@ protected void buttonClicked(ButtonElement button) { break; } case MODS_FOLDER_BUTTON_ID: { - File modsFolder = new File(FabricLoader.getInstance().getGameDir().toFile(), "mods"); + File modsFolder = new File(loader.getGameDir().toFile(), "mods"); if (!modsFolder.exists()) { modsFolder.mkdirs(); } @@ -338,7 +336,12 @@ public void render(int mouseX, int mouseY, float delta) { this.drawStringCenteredNoShadow(font, this.textTitle, this.modList.getWidth() / 2, 8, 0xffffff); super.render(mouseX, mouseY, delta); if (showModCount || !filterOptionsShown) { - String showModCountString = i18n.translateKeyAndFormat("modmenu.showingMods", NumberFormat.getInstance().format(modList.getDisplayedCount()) + "/" + NumberFormat.getInstance().format(FabricLoader.getInstance().getAllMods().size())); + String showModCountString; + if (config.getShowLibraries() && modList.getDisplayedCount() == 0) { + showModCountString = computeLibraryCountText(false); + } else { + showModCountString = computeModCountText(true, false); + } this.drawStringNoShadow(font, showModCountString, searchBoxX, 52, 0xFFFFFF); } if (selectedEntry != null) { @@ -463,6 +466,54 @@ public void updateSelectedEntry(ModListEntry entry) { } } + private int[] formatModCount(Set set, boolean allVisible) { + int visible = this.modList.getDisplayedCountFor(set); + int total = set.size(); + if (visible == total || allVisible) { + return new int[]{total}; + } else { + return new int[]{visible, total}; + } + } + + private String computeModCountText(boolean includeLibs, boolean onInit) { + Set rootModIds = loader.getAllMods().stream() + .map(container -> container.getMetadata().getId()) + .filter(id -> !ModMenu.LIBRARY_MODS.contains(id)) + .collect(Collectors.toSet()); + + int[] rootMods = formatModCount(rootModIds, onInit); + + if (includeLibs && !onInit && config.getShowLibraries()) { + Set rootLibIds = loader.getAllMods().stream() + .map(container -> container.getMetadata().getId()) + .filter(ModMenu.LIBRARY_MODS::contains) + .collect(Collectors.toSet()); + + int[] rootLibs = formatModCount(rootLibIds, false); + + if (rootLibs.length == 1 && rootLibs[0] != 0 || rootLibs.length == 2 && rootLibs[1] != 0) { + return TranslationUtil.translateNumeric("modmenu.showingModsLibraries", rootMods, rootLibs); + } + } + + return TranslationUtil.translateNumeric("modmenu.showingMods", rootMods); + } + + private String computeLibraryCountText(boolean onInit) { + if (!onInit && config.getShowLibraries()) { + Set rootLibIds = loader.getAllMods().stream() + .map(container -> container.getMetadata().getId()) + .filter(ModMenu.LIBRARY_MODS::contains) + .collect(Collectors.toSet()); + + int[] rootLibs = formatModCount(rootLibIds, false); + return TranslationUtil.translateNumeric("modmenu.showingLibraries", rootLibs); + } else { + return ""; + } + } + public double getScrollPercent() { return scrollPercent; } @@ -475,14 +526,8 @@ public String getSearchInput() { return searchBox.getText(); } - public boolean showingFilterOptions() { - return filterOptionsShown; - } - public void renderTooltip(List list, int i, int j) { if (!list.isEmpty()) { - FontRenderer font = this.fontRenderer; - GLRenderer.pushFrame(); GLRenderer.disableState(State.DEPTH_TEST); int k = 0; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java index 818d8b8a1..9efb978ed 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java @@ -267,12 +267,10 @@ public void mouseClicked(int double_1, int double_2, int int_1) { } @Override - public boolean mouseScrolled(double mouseX, double mouseY, double scrollDelta) { + public void mouseScrolled(double mouseX, double mouseY, double scrollDelta) { if (this.isMouseOver(mouseX, mouseY)) { setScrollAmount(getScrollAmount() - scrollDelta * 20.0); - return true; } - return false; } public final ModListEntry getEntryAtPos(double x, double y) { @@ -330,7 +328,13 @@ void cacheModIcon(Path path, BufferedImage tex) { this.modIconsCache.put(path, tex); } - public Set getCurrentModSet() { - return addedMods; + public int getDisplayedCountFor(Set set) { + int count = 0; + for (ModListEntry c : children()) { + if (set.contains(c.getMetadata().getId())) { + count++; + } + } + return count; } } diff --git a/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java b/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java new file mode 100644 index 000000000..a7b9c22b5 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java @@ -0,0 +1,63 @@ +package io.github.prospector.modmenu.util; + +import io.github.prospector.modmenu.ModMenu; +import net.minecraft.core.lang.I18n; + +import java.text.NumberFormat; +import java.util.Arrays; + +public class TranslationUtil { + public static String translateNumeric(String key, int[]... args) { + Object[] realArgs = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + NumberFormat nf = NumberFormat.getInstance(); + if (args[i].length == 1) { + realArgs[i] = nf.format(args[i][0]); + } else { + assert args[i].length == 2; + realArgs[i] = nf.format(args[i][0]) + "/" + nf.format(args[i][1]); + } + } + + int[] override = new int[args.length]; + Arrays.fill(override, -1); + for (int i = 0; i < args.length; i++) { + int[] arg = args[i]; + if (arg == null) { + throw new NullPointerException("args[" + i + "]"); + } + if (arg.length == 1) { + if (arg[0] == 1) { + override[i] = 1; + } else { + override[i] = -1; + } + } + } + + String lastKey = key; + I18n i18n = I18n.getInstance(); + for (int flags = (1 << args.length) - 1; flags >= 0; flags--) { + StringBuilder fullKey = new StringBuilder(key); + for (int i = 0; i < args.length; i++) { + fullKey.append('.'); + if (((flags & (1 << i)) != 0) && override[i] != -1) { + fullKey.append(override[i]); + } else { + fullKey.append('n'); + } + } + lastKey = fullKey.toString(); + + String translated = i18n.translateKey(lastKey); + if (!translated.equals(lastKey)) { + return i18n.translateKeyAndFormat(lastKey, realArgs); + } + } + return i18n.translateKeyAndFormat(lastKey, realArgs); + } + + public static String translationKeyOf(String type, String id) { + return type + "." + ModMenu.MOD_ID + "." + id; + } +} \ No newline at end of file diff --git a/src/main/resources/assets/modmenu/lang/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang index 82e779c3e..2185f5d67 100644 --- a/src/main/resources/assets/modmenu/lang/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US.lang @@ -1,17 +1,81 @@ modmenu.title=Mods +modmenu.nameTranslation.modmenu=Mod Menu +modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. + modmenu.loaded=(%s Loaded) + +modmenu.search=Search for mods +modmenu.searchTerms.library=api library +modmenu.searchTerms.clientside=clientside gameside +modmenu.searchTerms.deprecated=deprecated outdated old +modmenu.searchTerms.patchwork=patchwork forge fml +modmenu.searchTerms.configurable=configurations configs configures configurable options settings + +modmenu.toggleFilterOptions=Toggle Filter Options +modmenu.showingMods.n=Showing %s mods +modmenu.showingMods.1=Showing %s mod +modmenu.showingLibraries.n=Showing %s libraries +modmenu.showingLibraries.1=Showing %s library +modmenu.showingModsLibraries.n.n=Showing %s mods and %s libraries +modmenu.showingModsLibraries.n.1=Showing %s mods and %s library +modmenu.showingModsLibraries.1.n=Showing %s mod and %s libraries +modmenu.showingModsLibraries.1.1=Showing %s mod and %s library + +modmenu.badge.clientsideOnly=Client +modmenu.badge.library=Library +modmenu.badge.deprecated=Deprecated +modmenu.badge.minecraft=Minecraft +modmenu.badge.forge=Forge + +modmenu.modIdToolTip=Mod ID: %s +modmenu.authorPrefix=By %s modmenu.config=Edit Config -modmenu.modsFolder=Open Mods Folder -modmenu.configsFolder=Open Configs Folder modmenu.configure=Configure... -modmenu.modIdToolTip=Mod ID: %s modmenu.website=Website modmenu.issues=Issues -modmenu.authorPrefix=By %s + +modmenu.credits=Credits: +modmenu.viewCredits=View Credits modmenu.license=License: -modmenu.search=Search for mods -modmenu.toggleFilterOptions=Toggle Filter Options -modmenu.showingMods=Showing %s Mods +modmenu.links=Links: +modmenu.sources=Source + +modmenu.bluesky=Bluesky +modmenu.buymeacoffee=Buy Me a Coffee +modmenu.coindrop=Coindrop +modmenu.crowdin=Crowdin +modmenu.curseforge=CurseForge +modmenu.discord=Discord +modmenu.donate=Donate +modmenu.flattr=Flattr +modmenu.github_releases=GitHub Releases +modmenu.github_sponsors=GitHub Sponsors +modmenu.kofi=Ko-fi +modmenu.liberapay=Liberapay +modmenu.mastodon=Mastodon +modmenu.modrinth=Modrinth +modmenu.opencollective=Open Collective +modmenu.patreon=Patreon +modmenu.paypal=PayPal +modmenu.reddit=Reddit +modmenu.twitch=Twitch +modmenu.wiki=Wiki +modmenu.twitter=X (Twitter) +modmenu.youtube=YouTube + +modmenu.authors=Authors: +modmenu.contributors=Contributors: + +modmenu.modsFolder=Open Mods Folder +modmenu.configsFolder=Open Configs Folder + +modmenu.nameTranslation.minecraft=Better than Adventure! +modmenu.descriptionTranslation.minecraft=The base game. +modmenu.nameTranslation.fabricloader=Fabric Loader +modmenu.descriptionTranslation.fabricloader=The base mod loader. +modmenu.nameTranslation.java=Java +modmenu.descriptionTranslation.java=The Java runtime environment. +modmenu.javaDistributionName=Running: %s modmenu.options=Mod Menu Options option.modmenu.sorting=Sort @@ -56,55 +120,3 @@ option.modmenu.translate_descriptions.false=Not Localized option.modmenu.quick_configure=Quick Configure option.modmenu.quick_configure.true=Enabled option.modmenu.quick_configure.false=Disabled - -modmenu.credits=Credits: -modmenu.authors=Authors: -modmenu.contributors=Contributors: -modmenu.viewCredits=View Credits - -modmenu.searchTerms.library=api library -modmenu.searchTerms.clientside=clientside gameside -modmenu.searchTerms.deprecated=deprecated outdated old -modmenu.searchTerms.patchwork=patchwork forge fml -modmenu.searchTerms.configurable=configurations configs configures configurable options settings - -modmenu.badge.clientsideOnly=Client -modmenu.badge.library=Library -modmenu.badge.deprecated=Deprecated -modmenu.badge.minecraft=Minecraft -modmenu.badge.forge=Forge - -modmenu.links=Links: -modmenu.sources=Source -modmenu.bluesky=Bluesky -modmenu.buymeacoffee=Buy Me a Coffee -modmenu.coindrop=Coindrop -modmenu.crowdin=Crowdin -modmenu.curseforge=CurseForge -modmenu.discord=Discord -modmenu.donate=Donate -modmenu.flattr=Flattr -modmenu.github_releases=GitHub Releases -modmenu.github_sponsors=GitHub Sponsors -modmenu.kofi=Ko-fi -modmenu.liberapay=Liberapay -modmenu.mastodon=Mastodon -modmenu.modrinth=Modrinth -modmenu.opencollective=Open Collective -modmenu.patreon=Patreon -modmenu.paypal=PayPal -modmenu.reddit=Reddit -modmenu.twitch=Twitch -modmenu.wiki=Wiki -modmenu.twitter=X (Twitter) -modmenu.youtube=YouTube - -modmenu.nameTranslation.minecraft=Better than Adventure! -modmenu.descriptionTranslation.minecraft=The base game. -modmenu.nameTranslation.fabricloader=Fabric Loader -modmenu.descriptionTranslation.fabricloader=The base mod loader. -modmenu.nameTranslation.modmenu=Mod Menu -modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. -modmenu.nameTranslation.java=Java -modmenu.descriptionTranslation.java=The Java runtime environment. -modmenu.javaDistributionName=Running: %s From 35091e01b9244a78ec7c8409680f9c1d200c2ec7 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:25:50 +0300 Subject: [PATCH 49/59] ukrainian --- .../prospector/modmenu/gui/ModListEntry.java | 43 +++--- .../resources/assets/modmenu/lang/uk_UA.lang | 122 ++++++++++++++++++ 2 files changed, 145 insertions(+), 20 deletions(-) create mode 100644 src/main/resources/assets/modmenu/lang/uk_UA.lang diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 8314ad97b..2e4ecdc7c 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -31,7 +31,6 @@ import java.util.Objects; public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry { - public static final String UNKNOWN_ICON = "/assets/modmenu/unknown_icon.png"; private static final Logger LOGGER = LoggerFactory.getLogger(ModMenu.MOD_ID); protected final Minecraft client; @@ -187,38 +186,42 @@ static void internalRender(int y, int x) { private BufferedImage createIcon() { try { - Path path = container.getPath(metadata.getIconPath(0).orElse("assets/" + metadata.getId() + "/icon.png")); - BufferedImage cached = this.list.getCachedModIcon(path); + Path path = container.findPath(metadata.getIconPath(0).orElse("assets/" + metadata.getId() + "/icon.png")).orElse(null); + BufferedImage cached = path != null ? this.list.getCachedModIcon(path) : null; if (cached != null) { return cached; } - if (!Files.exists(path)) { + if (path == null || !Files.exists(path)) { ModContainer modMenu = FabricLoader.getInstance().getModContainer(ModMenu.MOD_ID).orElseThrow(IllegalAccessError::new); + String icon; if (metadata.getId().equals("minecraft")) { - path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/mc_icon.png"); + icon = "assets/" + ModMenu.MOD_ID + "/mc_icon.png"; } else if (metadata.getId().equals("java")) { - path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/java_icon.png"); + icon = "assets/" + ModMenu.MOD_ID + "/java_icon.png"; } else { - path = modMenu.getPath("assets/" + ModMenu.MOD_ID + "/unknown_icon.png"); + icon = "assets/" + ModMenu.MOD_ID + "/unknown_icon.png"; } + path = modMenu.findPath(icon).orElse(null); } - cached = this.list.getCachedModIcon(path); - if (cached != null) { - return cached; - } - try (InputStream inputStream = Files.newInputStream(path)) { - BufferedImage image = ImageIO.read(Objects.requireNonNull(inputStream)); - if (image.getHeight() != image.getWidth()) - throw new IllegalStateException("Must be square icon"); - this.list.cacheModIcon(path, image); - return image; + if (path != null) { + cached = this.list.getCachedModIcon(path); + if (cached != null) { + return cached; + } + try (InputStream inputStream = Files.newInputStream(path)) { + BufferedImage image = ImageIO.read(Objects.requireNonNull(inputStream)); + if (image.getHeight() != image.getWidth()) + throw new IllegalStateException("Must be square icon"); + this.list.cacheModIcon(path, image); + return image; + } } - } catch (Throwable t) { LOGGER.error("Invalid icon for mod {}", this.container.getMetadata().getName(), t); return null; } - } + return null; + } @Override public void mouseClicked(int v, int v1, int i) { @@ -250,7 +253,7 @@ public void bindIconTexture() { if (icon != null) { this.iconLocation = this.client.textureManager.loadBufferedTexture(icon).id(); } else { - this.iconLocation = this.client.textureManager.loadTexture(UNKNOWN_ICON).id(); + this.iconLocation = this.client.textureManager.loadTexture("/assets/modmenu/unknown_icon.png").id(); } } this.client.textureManager.bindTexture(this.iconLocation); diff --git a/src/main/resources/assets/modmenu/lang/uk_UA.lang b/src/main/resources/assets/modmenu/lang/uk_UA.lang new file mode 100644 index 000000000..748881099 --- /dev/null +++ b/src/main/resources/assets/modmenu/lang/uk_UA.lang @@ -0,0 +1,122 @@ +modmenu.title=Моди +modmenu.nameTranslation.modmenu=Mod Menu +modmenu.descriptionTranslation.modmenu=Додає меню для перегляду встановлених модів. + +modmenu.loaded=(%s) + +modmenu.search=Пошук +modmenu.searchTerms.library=api бібліотека library +modmenu.searchTerms.clientside=сторонаклієнта клієт client +modmenu.searchTerms.deprecated=застарілий віджилий старий old outdated deprecated +modmenu.searchTerms.patchwork=роботапатча forge fml patch +modmenu.searchTerms.configurable=конфігурація налаштування налаштувань параметри настройки config settings options + +modmenu.toggleFilterOptions=Фільтри +modmenu.showingMods.n=Показано %s модів +modmenu.showingMods.1=Показано %s мод +modmenu.showingLibraries.n=Показано %s бібліотек +modmenu.showingLibraries.1=Показано %s бібліотеку +modmenu.showingModsLibraries.n.n=Показано %s модів і %s бібліотек +modmenu.showingModsLibraries.n.1=Показано %s модів і %s бібліотеку +modmenu.showingModsLibraries.1.n=Показано %s мод і %s бібліотек +modmenu.showingModsLibraries.1.1=Показано %s мод і %s бібліотеку + +modmenu.badge.clientsideOnly=Клієнт +modmenu.badge.library=Бібліотека +modmenu.badge.deprecated=Застарілий +modmenu.badge.minecraft=Minecraft +modmenu.badge.forge=Forge + +modmenu.modIdToolTip=ID мода: %s +modmenu.authorPrefix=Від %s +modmenu.config=Редагувати налаштування +modmenu.configure=Налаштування… +modmenu.website=Сайт мода +modmenu.issues=Звітувати помилку + +modmenu.credits=Згадки: +modmenu.viewCredits=Показати авторів +modmenu.license=Ліцензія: +modmenu.links=Посилання: +modmenu.sources=Початковий код + +modmenu.bluesky=Bluesky +modmenu.buymeacoffee=Buy Me a Coffee +modmenu.coindrop=Coindrop +modmenu.crowdin=Crowdin +modmenu.curseforge=CurseForge +modmenu.discord=Discord +modmenu.donate=Підтримати +modmenu.flattr=Flattr +modmenu.github_releases=Випуски на GitHub +modmenu.github_sponsors=Спонсори GitHub +modmenu.kofi=Ko-fi +modmenu.liberapay=Liberapay +modmenu.mastodon=Mastodon +modmenu.modrinth=Modrinth +modmenu.opencollective=Open Collective +modmenu.patreon=Patreon +modmenu.paypal=PayPal +modmenu.reddit=Reddit +modmenu.twitch=Twitch +modmenu.wiki=Вікі +modmenu.twitter=Х (Twitter) +modmenu.youtube=YouTube + +modmenu.authors=Автори: +modmenu.contributors=Дописувачі: + +modmenu.modsFolder=Відкрити теку модів +modmenu.configsFolder=Відкрити теку налаштувань + +modmenu.nameTranslation.minecraft=Better than Adventure! +modmenu.descriptionTranslation.minecraft=Усталена гра. +modmenu.nameTranslation.fabricloader=Завантажувач Fabric +modmenu.descriptionTranslation.fabricloader=Усталений завантажувач модів. +modmenu.nameTranslation.java=Java +modmenu.descriptionTranslation.java=Середовище виконання Java. +modmenu.javaDistributionName=Запуск: %s + +modmenu.options=Налаштування Mod Menu +option.modmenu.sorting=Сортування +option.modmenu.sorting.ascending=А-Я +option.modmenu.sorting.descending=Я-А +option.modmenu.show_libraries=Бібліотеки +option.modmenu.show_libraries.true=Показано +option.modmenu.show_libraries.false=Приховано +option.modmenu.hide_config_buttons=Кнопки налашт. +option.modmenu.hide_config_buttons.true=Приховано +option.modmenu.hide_config_buttons.false=Показано +option.modmenu.hide_badges=Значки модів +option.modmenu.hide_badges.true=Приховано +option.modmenu.hide_badges.false=Показано +option.modmenu.hide_mod_links=Посилання +option.modmenu.hide_mod_links.true=Приховано +option.modmenu.hide_mod_links.false=Показано +option.modmenu.hide_mod_credits=Згадки +option.modmenu.hide_mod_credits.true=Приховано +option.modmenu.hide_mod_credits.false=Показано +option.modmenu.hide_mod_license=Ліцензія +option.modmenu.hide_mod_license.true=Приховано +option.modmenu.hide_mod_license.false=Показано +option.modmenu.easter_eggs=Прихованки +option.modmenu.easter_eggs.true=УВІМК. +option.modmenu.easter_eggs.false=ВИМК. +option.modmenu.mods_button_style=Головне меню +option.modmenu.mods_button_style.insert=Окремо +option.modmenu.mods_button_style.icon=Значок +option.modmenu.game_menu_button_style=Ігрове меню +option.modmenu.game_menu_button_style.insert=Окремо +option.modmenu.game_menu_button_style.icon=Значок +option.modmenu.random_java_colors=Колір Java +option.modmenu.random_java_colors.true=Постачальник +option.modmenu.random_java_colors.false=Червоний +option.modmenu.translate_names=Назви +option.modmenu.translate_names.true=Перекладено +option.modmenu.translate_names.false=Не перекладено +option.modmenu.translate_descriptions=Описи +option.modmenu.translate_descriptions.true=Перекладено +option.modmenu.translate_descriptions.false=Не перекладено +option.modmenu.quick_configure=Швидке налаштування +option.modmenu.quick_configure.true=УВІМК. +option.modmenu.quick_configure.false=ВИМК. From 982d3d6c982e7d20aa084b2f0e1af48904979b1e Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:41:33 +0300 Subject: [PATCH 50/59] update checker and api --- .../io/github/prospector/modmenu/ModMenu.java | 40 ++- .../modmenu/api/ConfigScreenFactory.java | 8 + .../prospector/modmenu/api/ModMenuApi.java | 98 +++-- .../prospector/modmenu/api/UpdateChannel.java | 19 + .../prospector/modmenu/api/UpdateChecker.java | 16 + .../prospector/modmenu/api/UpdateInfo.java | 29 ++ .../modmenu/config/ModMenuConfig.java | 61 +++- .../config/ModMenuConfigIntegration.java | 18 - .../modmenu/config/ModMenuConfigScreen.java | 93 +++-- .../modmenu/gui/DescriptionListWidget.java | 66 +++- .../prospector/modmenu/gui/ModListEntry.java | 20 +- .../modmenu/gui/ModMenuButtonWidget.java | 18 +- .../gui/ModMenuTexturedButtonWidget.java | 11 + .../modmenu/impl/ModMenuApiImpl.java | 12 + .../modmenu/mixin/ScreenMainMenuMixin.java | 29 +- .../modmenu/util/BadgeRenderer.java | 4 + .../prospector/modmenu/util/HttpUtil.java | 50 +++ .../modmenu/util/NullScreenFactory.java | 15 + .../modmenu/util/UpdateCheckerUtil.java | 340 ++++++++++++++++++ .../prospector/modmenu/util/VersionUtil.java | 30 ++ .../modmenu/util/mod/FabricMod.java | 91 +++++ .../prospector/modmenu/util/mod/Mod.java | 30 ++ .../modmenu/util/mod/ModrinthUpdateInfo.java | 31 ++ .../resources/assets/modmenu/lang/en_US.lang | 17 + .../resources/assets/modmenu/lang/uk_UA.lang | 17 + .../modmenu/textures/gui/update_available.png | Bin 0 -> 191 bytes src/main/resources/fabric.mod.json | 2 +- 27 files changed, 1029 insertions(+), 136 deletions(-) create mode 100644 src/main/java/io/github/prospector/modmenu/api/ConfigScreenFactory.java create mode 100644 src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java create mode 100644 src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java create mode 100644 src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java delete mode 100644 src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java create mode 100644 src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/HttpUtil.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/NullScreenFactory.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/VersionUtil.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/mod/Mod.java create mode 100644 src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java create mode 100644 src/main/resources/assets/modmenu/textures/gui/update_available.png diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 3eabe099f..61708b96b 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -5,6 +5,9 @@ import com.google.gson.GsonBuilder; import io.github.prospector.modmenu.api.ModMenuApi; import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.util.UpdateCheckerUtil; +import io.github.prospector.modmenu.util.mod.FabricMod; +import io.github.prospector.modmenu.util.mod.Mod; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; @@ -15,15 +18,18 @@ import net.minecraft.client.gui.options.ScreenOptions; import net.minecraft.client.gui.options.data.OptionsPages; import net.minecraft.core.data.registry.Registries; +import net.minecraft.core.lang.I18n; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.NumberFormat; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; public class ModMenu implements ModInitializer { public static final String MOD_ID = "modmenu"; + public static final String GITHUB_REF = "Turnip-Labs/ModMenu"; static { Registries.NAMESPACES.register(MOD_ID, MOD_ID); } @@ -38,6 +44,8 @@ public class ModMenu implements ModInitializer { public static final Map>> CUSTOM_BADGE_MODS = new HashMap<>(); public static final Map> PARENT_MAP = new HashMap<>(); private static Map> configScreenFactories = new HashMap<>(); + public static final Map MODS = new ConcurrentHashMap<>(); + public static final boolean DEV_ENVIRONMENT = FabricLoader.getInstance().isDevelopmentEnvironment(); public static boolean hasConfigScreenFactory(String modid) { return configScreenFactories.containsKey(modid); @@ -53,6 +61,14 @@ public static void openConfigScreen(String modid) { if (opener != null) opener.run(); } + public static String createModsButtonText(boolean showCount) { + String baseText = I18n.getInstance().translateKey("modmenu.title"); + if (showCount) { + return baseText + " " + I18n.getInstance().translateKeyAndFormat("modmenu.loaded", getFormattedModCount()); + } + return baseText; + } + public static void addLegacyConfigScreenTask(String modid, Runnable task) { LEGACY_CONFIG_SCREEN_TASKS.putIfAbsent(modid, task); } @@ -75,10 +91,13 @@ public void onInitialize() { loader.getEntrypointContainers("modmenu", ModMenuApi.class).forEach(entrypoint -> { ModMenuApi api = entrypoint.getEntrypoint(); ModContainer mod = entrypoint.getProvider(); - try { - api.getClass().getDeclaredMethod("getConfigScreenFactory"); // Make sure the method is implemented - configScreenFactories.put(mod.getMetadata().getId(), api.getConfigScreenFactory()); - } catch (NoSuchMethodException ignored) {} + try { + api.getClass().getDeclaredMethod("getModConfigScreenFactory"); + var factory = api.getModConfigScreenFactory(); + if (factory != null) { + configScreenFactories.put(mod.getMetadata().getId(), factory::create); + } + } catch (NoSuchMethodException ignored) {} api.attachCustomBadges((name, outlineColor, fillColor) -> { Map> map = new HashMap<>(); map.put(name, new AbstractMap.SimpleEntry<>(outlineColor, fillColor)); @@ -90,6 +109,10 @@ public void onInitialize() { for (ModContainer mod : mods) { ModMetadata metadata = mod.getMetadata(); String id = metadata.getId(); + + MODS.put(id, new FabricMod(mod)); + UpdateCheckerUtil.checkForUpdates(); + // API badges if (metadata.containsCustomValue("modmenu:api") && metadata.getCustomValue("modmenu:api").getAsBoolean()) { addLibraryMod(id); @@ -138,6 +161,15 @@ public void onInitialize() { } } + public static boolean areModUpdatesAvailable() { + for (Mod mod : MODS.values()) { + if (mod.getUpdateInfo() != null && mod.getUpdateInfo().isUpdateAvailable()) { + return true; + } + } + return false; + } + public static String getFormattedModCount() { return NumberFormat.getInstance().format(FabricLoader.getInstance().getAllMods().size()); } diff --git a/src/main/java/io/github/prospector/modmenu/api/ConfigScreenFactory.java b/src/main/java/io/github/prospector/modmenu/api/ConfigScreenFactory.java new file mode 100644 index 000000000..e9e8cceaf --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/api/ConfigScreenFactory.java @@ -0,0 +1,8 @@ +package io.github.prospector.modmenu.api; + +import net.minecraft.client.gui.Screen; + +@FunctionalInterface +public interface ConfigScreenFactory { + S create(Screen parent); +} diff --git a/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java b/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java index 3e86572e6..b8a40956c 100644 --- a/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java +++ b/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java @@ -1,66 +1,88 @@ package io.github.prospector.modmenu.api; import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.gui.ModListScreen; +import io.github.prospector.modmenu.util.NullScreenFactory; import io.github.prospector.modmenu.util.TriConsumer; import net.minecraft.client.gui.Screen; -import org.jetbrains.annotations.ApiStatus; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Supplier; +import java.util.Map; +@SuppressWarnings("unused") public interface ModMenuApi { /** - * Replaced with {@link ModMenuApi#getConfigScreen(Screen)}, with - * the ModMenuApi implemented onto a class that is added as an - * entry point to your fabric mod metadata. + * Used for creating a {@link Screen} instance of the Mod Menu + * "Mods" screen * - * @deprecated Will be removed in 1.15 snapshots. + * @param previous The screen before opening + * @return A "Mods" Screen */ - @Deprecated - static void addConfigOverride(String modid, Runnable action) { - ModMenu.addLegacyConfigScreenTask(modid, action); + static Screen createModsScreen(Screen previous) { + return new ModListScreen(previous); } /** - * Used to determine the owner of this API implementation. + * Used for creating text just like what would appear + * on a Mod Menu Mods button * - * @deprecated This method is deprecated as Fabric Loader - * itself provides provider info for entrypoints. + * @return The text that would be displayed on a Mods button */ - @Deprecated - String getModId(); + static String createModsButtonText() { + return ModMenu.createModsButtonText(true); + } /** - * Replaced with {@link ModMenuApi#getConfigScreenFactory()}, which - * now allows ModMenu to open the screen for you, rather than depending - * on you to open it, and gets rid of the messy Optional->Supplier wrapping. + * Used to construct a new config screen instance when your mod's + * configuration button is selected on the mod menu screen. The + * screen instance parameter is the active mod menu screen. * - * @deprecated For internal use only. + * @return A factory for constructing config screen instances. */ - @Deprecated - @ApiStatus.Internal - default Optional> getConfigScreen(Screen screen) { - return Optional.empty(); + default ConfigScreenFactory getModConfigScreenFactory() { + return new NullScreenFactory<>(); } /** - * Used to construct a new config screen instance when your mod's - * configuration button is selected on the mod menu screen. The - * screen instance parameter is the active mod menu screen. + * Used for mods that have their own update checking logic. + * By returning your own {@link UpdateChecker} instance, you can override ModMenus built-in update checking logic. + * + * @return An {@link UpdateChecker} or null if ModMenu should handle update checking. + */ + default UpdateChecker getUpdateChecker() { + return null; + } + + /** + * Used to provide config screen factories for other mods. This takes second + * priority to a mod's own config screen factory provider. For example, if + * mod `xyz` supplies a config screen factory, mod `abc` providing a config + * screen to `xyz` will be pointless, as the one provided by `xyz` will be + * used. + *

+ * This method is NOT meant to be used to add a config screen factory to + * your own mod. * - * @return A factory function for constructing config screen instances. + * @return a map of mod ids to screen factories. */ - default Function getConfigScreenFactory() { - return screen -> getConfigScreen(screen).map(Supplier::get).orElse(null); + default Map> getProvidedConfigScreenFactories() { + return Map.of(); } + /** + * Used to provide update checkers for other mods. A mod registering its own + * update checker will take priority over any provided ones should both exist. + * + * @return a map of mod ids to update checkers. + */ + default Map getProvidedUpdateCheckers() { + return Map.of(); + } - /** - * Add custom badges for mods to use. - * - * @param consumer the consumer used to add new badges. - */ - default void attachCustomBadges(TriConsumer consumer) { - } -} + /** + * Add custom badges for mods to use. + * + * @param consumer the consumer used to add new badges. + */ + default void attachCustomBadges(TriConsumer consumer) { + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java b/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java new file mode 100644 index 000000000..add019d25 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java @@ -0,0 +1,19 @@ +package io.github.prospector.modmenu.api; + +import io.github.prospector.modmenu.config.ModMenuConfigManager; + +/** + * Supported update channels, in ascending order by stability. + */ +public enum UpdateChannel { + ALPHA, + BETA, + RELEASE; + + /** + * @return the user's preferred update channel. + */ + public static UpdateChannel getUserPreference() { + return UpdateChannel.valueOf(ModMenuConfigManager.getConfig().getUpdateChannel().name()); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java b/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java new file mode 100644 index 000000000..549315713 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java @@ -0,0 +1,16 @@ +package io.github.prospector.modmenu.api; + +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("unused") +public interface UpdateChecker { + /** + * Gets called when ModMenu is checking for updates. + * This is done in a separate thread, so this call can/should be blocking. + * + *

Your update checker should aim to return an update on the same or a more stable channel than the user's preference which you can get via {@link UpdateChannel#getUserPreference()}.

+ * + * @return The update info + */ + @Nullable UpdateInfo checkForUpdates(); +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java b/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java new file mode 100644 index 000000000..9f38f63e9 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java @@ -0,0 +1,29 @@ +package io.github.prospector.modmenu.api; + +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("unused") +public interface UpdateInfo { + /** + * @return If an update for the mod is available. + */ + boolean isUpdateAvailable(); + + /** + * @return The message that is getting displayed when an update is available or null to let ModMenu handle displaying the message. + */ + @Nullable + default String getUpdateMessage() { + return null; + } + + /** + * @return The URL to the mod download. + */ + String getDownloadLink(); + + /** + * @return The update channel this update is available for. + */ + UpdateChannel getUpdateChannel(); +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index fa7492442..4e21303f8 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -19,7 +19,10 @@ public class ModMenuConfig { private boolean randomJavaColors = true; private boolean translateNames = true; private boolean translateDescriptions = true; + private boolean updateChecker = true; + private boolean buttonUpdateBadge = true; private boolean quickConfigure = true; + private UpdateChannel updateChannel = UpdateChannel.RELEASE; public void toggleSortMode() { this.sorting = next(this.sorting); @@ -86,11 +89,26 @@ public void toggleTranslateDescriptions() { ModMenuConfigManager.save(); } + public void toggleUpdateChecker() { + this.updateChecker = !this.updateChecker; + ModMenuConfigManager.save(); + } + + public void toggleButtonUpdateBadge() { + this.buttonUpdateBadge = !this.buttonUpdateBadge; + ModMenuConfigManager.save(); + } + public void toggleQuickConfigure() { this.quickConfigure = !this.quickConfigure; ModMenuConfigManager.save(); } + public void toggleUpdateChannel() { + this.updateChannel = next(this.updateChannel); + ModMenuConfigManager.save(); + } + public Sorting getSorting() { return sorting; } @@ -143,10 +161,22 @@ public boolean getTranslateDescriptions() { return translateDescriptions; } + public boolean getUpdateChecker() { + return updateChecker; + } + + public boolean getButtonUpdateBadge() { + return buttonUpdateBadge; + } + public boolean getQuickConfigure() { return quickConfigure; } + public UpdateChannel getUpdateChannel() { + return updateChannel; + } + public String getSortingDisplayString() { return optionString("sorting", this.sorting.getName()); } @@ -199,10 +229,22 @@ public String getTranslateDescriptionsDisplayString() { return optionString("translate_descriptions", I18n.getInstance().translateKey("option.modmenu.translate_descriptions." + this.translateDescriptions)); } + public String getUpdateCheckerDisplayString() { + return optionString("update_checker", I18n.getInstance().translateKey("option.modmenu.update_checker." + this.updateChecker)); + } + + public String getButtonUpdateBadgeDisplayString() { + return optionString("button_update_badge", I18n.getInstance().translateKey("option.modmenu.button_update_badge." + this.buttonUpdateBadge)); + } + public String getQuickConfigureDisplayString() { return optionString("quick_configure", I18n.getInstance().translateKey("option.modmenu.quick_configure." + this.quickConfigure)); } + public String getUpdateChannelDisplayString() { + return optionString("update_channel", this.updateChannel.getName()); + } + private static > T next(T current) { T[] values = current.getDeclaringClass().getEnumConstants(); return values[(current.ordinal() + 1) % values.length]; @@ -272,4 +314,21 @@ public String getKey() { return key; } } -} \ No newline at end of file + + public enum UpdateChannel implements OptionEnum { + RELEASE("option.modmenu.update_channel.release"), + BETA("option.modmenu.update_channel.beta"), + ALPHA("option.modmenu.update_channel.alpha"); + + final String key; + + UpdateChannel(String key) { + this.key = key; + } + + @Override + public String getKey() { + return key; + } + } +} diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java deleted file mode 100644 index 19019d8f1..000000000 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigIntegration.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.prospector.modmenu.config; - -import io.github.prospector.modmenu.api.ModMenuApi; -import net.minecraft.client.gui.Screen; - -import java.util.function.Function; - -public class ModMenuConfigIntegration implements ModMenuApi { - @Override - public String getModId() { - return "modmenu"; - } - - @Override - public Function getConfigScreenFactory() { - return ModMenuConfigScreen::new; - } -} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java index 2d93c45c0..8e4d5c005 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -141,7 +141,7 @@ public void init() { startY + 144, buttonWidth, buttonHeight, - config.getQuickConfigureDisplayString() + config.getUpdateCheckerDisplayString() )); this.add(new ButtonElement( @@ -150,7 +150,25 @@ public void init() { startY + 144, buttonWidth, buttonHeight, - config.getHideConfigButtonsDisplayString() + config.getButtonUpdateBadgeDisplayString() + )); + + this.add(new ButtonElement( + 114, + leftX, + startY + 168, + buttonWidth, + buttonHeight, + config.getQuickConfigureDisplayString() + )); + + this.add(new ButtonElement( + 115, + rightX, + startY + 168, + buttonWidth, + buttonHeight, + config.getUpdateChannelDisplayString() )); @@ -177,51 +195,30 @@ public void render(int mouseX, int mouseY, float delta) { @Override protected void buttonClicked(ButtonElement button) { ModMenuConfig config = ModMenuConfigManager.getConfig(); - - if (button.id == 100) { - config.toggleSortMode(); - button.displayString = config.getSortingDisplayString(); - } else if (button.id == 101) { - config.toggleShowLibraries(); - button.displayString = config.getShowLibrariesDisplayString(); - } else if (button.id == 102) { - config.toggleModsButtonStyle(); - button.displayString = config.getModsButtonStyleDisplayString(); - } else if (button.id == 103) { - config.toggleGameMenuButtonStyle(); - button.displayString = config.getGameMenuButtonStyleDisplayString(); - } else if (button.id == 104) { - config.toggleHideModLinks(); - button.displayString = config.getHideModLinksDisplayString(); - } else if (button.id == 105) { - config.toggleHideModLicense(); - button.displayString = config.getHideModLicenseDisplayString(); - } else if (button.id == 106) { - config.toggleHideBadges(); - button.displayString = config.getHideBadgesDisplayString(); - } else if (button.id == 107) { - config.toggleHideModCredits(); - button.displayString = config.getHideModCreditsDisplayString(); - } else if (button.id == 108) { - config.toggleEasterEggs(); - button.displayString = config.getEasterEggsDisplayString(); - } else if (button.id == 109) { - config.toggleRandomJavaColors(); - button.displayString = config.getRandomJavaColorsDisplayString(); - } else if (button.id == 110) { - config.toggleTranslateNames(); - button.displayString = config.getTranslateNamesDisplayString(); - } else if (button.id == 111) { - config.toggleTranslateDescriptions(); - button.displayString = config.getTranslateDescriptionsDisplayString(); - } else if (button.id == 112) { - config.toggleQuickConfigure(); - button.displayString = config.getQuickConfigureDisplayString(); - } else if (button.id == 113) { - config.toggleHideConfigButtons(); - button.displayString = config.getHideConfigButtonsDisplayString(); - } else if (button.id == 200) { - this.closeAndReturn(); + String displayString = switch (button.id) { + case 100 -> { config.toggleSortMode(); yield config.getSortingDisplayString(); } + case 101 -> { config.toggleShowLibraries(); yield config.getShowLibrariesDisplayString(); } + case 102 -> { config.toggleModsButtonStyle(); yield config.getModsButtonStyleDisplayString(); } + case 103 -> { config.toggleGameMenuButtonStyle(); yield config.getGameMenuButtonStyleDisplayString(); } + case 104 -> { config.toggleHideModLinks(); yield config.getHideModLinksDisplayString(); } + case 105 -> { config.toggleHideModLicense(); yield config.getHideModLicenseDisplayString(); } + case 106 -> { config.toggleHideBadges(); yield config.getHideBadgesDisplayString(); } + case 107 -> { config.toggleHideModCredits(); yield config.getHideModCreditsDisplayString(); } + case 108 -> { config.toggleEasterEggs(); yield config.getEasterEggsDisplayString(); } + case 109 -> { config.toggleRandomJavaColors(); yield config.getRandomJavaColorsDisplayString(); } + case 110 -> { config.toggleTranslateNames(); yield config.getTranslateNamesDisplayString(); } + case 111 -> { config.toggleTranslateDescriptions(); yield config.getTranslateDescriptionsDisplayString(); } + case 112 -> { config.toggleUpdateChecker(); yield config.getUpdateCheckerDisplayString(); } + case 113 -> { config.toggleButtonUpdateBadge(); yield config.getButtonUpdateBadgeDisplayString(); } + case 114 -> { config.toggleQuickConfigure(); yield config.getQuickConfigureDisplayString(); } + case 115 -> { config.toggleUpdateChannel(); yield config.getUpdateChannelDisplayString(); } + + case 200 -> { this.closeAndReturn(); yield null; } + default -> null; + }; + + if (displayString != null) { + button.displayString = displayString; } } @@ -238,4 +235,4 @@ private void closeAndReturn() { } this.mc.displayScreen(this.parent); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index d20343982..9c8b87f8a 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -1,8 +1,11 @@ package io.github.prospector.modmenu.gui; +import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.api.UpdateInfo; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.util.RenderUtils; +import io.github.prospector.modmenu.util.mod.Mod; import net.fabricmc.loader.api.metadata.ContactInformation; import net.fabricmc.loader.api.metadata.Person; import net.minecraft.client.Minecraft; @@ -152,6 +155,45 @@ public void render(int mouseX, int mouseY, float delta) { links.remove("homepage"); links.remove("issues"); + if (config.getUpdateChecker()) { + Mod mod = ModMenu.MODS.get(id); + if (mod != null) { + UpdateInfo updateInfo = mod.getUpdateInfo(); + if (updateInfo != null && updateInfo.isUpdateAvailable()) { + + children().add(new DescriptionEntry("")); + + int index = 0; + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, i18n.translateKey("modmenu.hasUpdate"), getRowWidth() - 11)) { + DescriptionEntry entry = new DescriptionEntry(line); + if (index == 0) { + entry.setUpdateTextEntry(); + } + children().add(entry); + index++; + } + + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, i18n.translateKey("modmenu.experimental"), getRowWidth() - 16)) { + children().add(new DescriptionEntry(" " + TextFormatting.ORANGE + line)); + } + + String updateMessage = updateInfo.getUpdateMessage(); + String downloadLink = updateInfo.getDownloadLink(); + if (updateMessage == null) { + updateMessage = i18n.translateKey("modmenu.downloadLink" + TextFormatting.UNDERLINE + TextFormatting.BLUE); + } + + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, updateMessage, getRowWidth() - 16)) { + if (downloadLink != null) { + children().add(new LinkEntry(line, downloadLink, 8)); + } else { + children().add(new DescriptionEntry(" " + line)); + } + } + } + } + } + if (!links.isEmpty() && !config.getHideModLinks()) { children().add(new DescriptionEntry("")); @@ -230,16 +272,34 @@ protected void renderHoleBackground(int y1, int y2, int startAlpha, int endAlpha parent.overlayBackground(left, y1, right, y2, 64, 64, 64, startAlpha, endAlpha); } - protected class DescriptionEntry extends Entry { + protected static class DescriptionEntry extends Entry { protected String text; + protected int indent; + public boolean updateTextEntry = false; - public DescriptionEntry(String text) { + public DescriptionEntry(String text, int indent) { this.text = text; + this.indent = indent; + } + + public DescriptionEntry(String text) { + this(text, 0); + } + + public void setUpdateTextEntry() { + this.updateTextEntry = true; } @Override public void render(int index, int y, int x, int itemWidth, int itemHeight, int mouseX, int mouseY, boolean isSelected, float delta) { - this.drawStringShadow(this.fontRenderer, text, x, y, 0xAAAAAA); + int renderX = x + indent; + + if (updateTextEntry) { + UpdateAvailableBadge.renderBadge(renderX, y + 1); + renderX += 11; + } + + this.drawStringShadow(this.fontRenderer, text, renderX, y, 0xAAAAAA); } } } diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 2e4ecdc7c..cdaf8a23c 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -1,12 +1,14 @@ package io.github.prospector.modmenu.gui; import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.api.UpdateInfo; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.entries.ParentEntry; import io.github.prospector.modmenu.util.BadgeRenderer; import io.github.prospector.modmenu.util.DrawingUtil; import io.github.prospector.modmenu.util.RenderUtils; +import io.github.prospector.modmenu.util.mod.Mod; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -46,7 +48,7 @@ public ModListEntry(Minecraft mc, ModContainer container, ModListWidget list) { this.list = list; this.metadata = container.getMetadata(); this.client = mc; - } + } @Override public void render(int index, int y, int x, int rowWidth, int rowHeight, int mouseX, int mouseY, boolean isSelected, float delta) { @@ -126,7 +128,6 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou FontRenderer font = this.fontRenderer; trimmedName = ModListScreen.getString(fontRenderer, name, trimmedName, maxNameWidth); this.drawStringNoShadow(font, trimmedName, x + 32 + 3, y + 1, 0xFFFFFF); - new BadgeRenderer(client, x + 32 + 3 + font.stringWidth(name) + 2, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); String descKey = "modmenu.descriptionTranslation." + id; String translatedDesc = i18n.translateKey(descKey); @@ -143,6 +144,21 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou description = (description != null ? description : "") + "\n" + javaDist; } + int currentX = x + 32 + 3 + font.stringWidth(trimmedName) + 2; + + if (config.getUpdateChecker() && config.getButtonUpdateBadge()) { + Mod modInstance = ModMenu.MODS.get(id); + if (modInstance != null) { + UpdateInfo updateInfo = modInstance.getUpdateInfo(); + if (updateInfo != null && updateInfo.isUpdateAvailable()) { + UpdateAvailableBadge.renderBadge(currentX, y + 1); + currentX += 12; + } + } + } + + new BadgeRenderer(client, currentX, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); + if (description != null) { int textX = x + 32 + 3 + 4; int textY = y + 9 + 2; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java index 8495e8680..530203bf3 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java @@ -1,9 +1,23 @@ package io.github.prospector.modmenu.gui; +import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfigManager; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; public class ModMenuButtonWidget extends ButtonElement { - public ModMenuButtonWidget(int buttonId, int x, int y, int width, int height, String text) { - super(buttonId, x, y, width, height, text); + public ModMenuButtonWidget(int id, int x, int y, int width, int height, String text) { + super(id, x, y, width, height, text); + } + + @Override + public void drawButton(Minecraft minecraft, int mouseX, int mouseY) { + super.drawButton(minecraft, mouseX, mouseY); + if (ModMenuConfigManager.getConfig().getButtonUpdateBadge() && ModMenu.areModUpdatesAvailable()) { + UpdateAvailableBadge.renderBadge( + this.xPosition + this.width - 13, + this.yPosition + (this.height / 2) - 4 + ); + } } } diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java index 55398e857..9a8d0daa2 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java @@ -1,5 +1,7 @@ package io.github.prospector.modmenu.gui; +import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.render.font.FontRenderer; @@ -80,6 +82,15 @@ public void render(Minecraft mc, int mouseX, int mouseY) { GLRenderer.popFrame(); + if (this.id == 1000) { + if (ModMenuConfigManager.getConfig().getButtonUpdateBadge() && ModMenu.areModUpdatesAvailable()) { + UpdateAvailableBadge.renderBadge( + this.xPosition + this.width - 5, + this.yPosition - 3 + ); + } + } + this.mouseDragged(mc, mouseX, mouseY); if (!this.enabled) { this.drawStringCenteredNoShadow(font, this.displayString, this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, 0xffa0a0a0); diff --git a/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java b/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java new file mode 100644 index 000000000..9c8dea65f --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java @@ -0,0 +1,12 @@ +package io.github.prospector.modmenu.impl; + +import io.github.prospector.modmenu.api.ModMenuApi; +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import io.github.prospector.modmenu.config.ModMenuConfigScreen; + +public class ModMenuApiImpl implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return ModMenuConfigScreen::new; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java index c4ffb639b..529478143 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java @@ -8,34 +8,25 @@ import io.github.prospector.modmenu.gui.ModMenuTexturedButtonWidget; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; -import net.minecraft.client.gui.ScreenMainMenu; +import net.minecraft.client.gui.ScreenPause; import net.minecraft.core.lang.I18n; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.Random; - -@Mixin(value = ScreenMainMenu.class, remap = false) -public class ScreenMainMenuMixin extends Screen { +@Mixin(value = ScreenPause.class, remap = false) +public class ScreenPauseMixin extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - ButtonElement texturePackButton = this.buttons.get(2); - I18n i18n = I18n.getInstance(); - ModMenuConfig config = ModMenuConfigManager.getConfig(); - if (config.getEasterEggs()) { - texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); - } - if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { - int newWidth = ((ButtonElementAccessor) texturePackButton).getWidth() / 2 - 1; - ((ButtonElementAccessor) texturePackButton).setWidth(newWidth); + if (ModMenuConfigManager.getConfig().getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { + I18n i18n = I18n.getInstance(); String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); - this.buttons.add(new ModMenuButtonWidget(100, this.width / 2 + 2, texturePackButton.yPosition, newWidth, 20, buttonText)); + this.buttons.add(new ModMenuButtonWidget(1000, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); } else { - this.buttons.removeIf(b -> b.id == 11); + this.buttons.removeIf(b -> b.id == 10); this.buttons.add(new ModMenuTexturedButtonWidget( - 100, this.width / 2 - 124, this.height / 4 + 132, 20, 20, 0, 0, + 1000, this.width / 2 - 124, this.height / 4 + 104, 20, 20, 0, 0, "/assets/" + ModMenu.MOD_ID + "/textures/gui/open_button.png", 32, 64 ) {}); } @@ -43,8 +34,8 @@ public class ScreenMainMenuMixin extends Screen { @Inject(method = "buttonClicked", at = @At("HEAD")) private void modmenu$onActionPerformed(ButtonElement button, CallbackInfo ci) { - if (button.id == 100) { - mc.displayScreen(new ModListScreen(this)); + if (button.id == 1000) { + this.mc.displayScreen(new ModListScreen(this)); } } } diff --git a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java index c2d8ed04f..ed130a0a8 100644 --- a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java +++ b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java @@ -83,4 +83,8 @@ public void drawBadge(String text, int outlineColor, int fillColor, int mouseX, public ModMetadata getMetadata() { return metadata; } + + public int getWidth() { + return badgeX - startX; + } } diff --git a/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java b/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java new file mode 100644 index 000000000..ce8e7ed73 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java @@ -0,0 +1,50 @@ +package io.github.prospector.modmenu.util; + +import io.github.prospector.modmenu.ModMenu; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.IOException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; + +public class HttpUtil { + private static final Duration TIMEOUT = Duration.ofSeconds(30); + private static final String USER_AGENT = buildUserAgent(); + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + .connectTimeout(TIMEOUT) + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + + private HttpUtil() { + } + + public static HttpResponse request( + HttpRequest.Builder builder, + HttpResponse.BodyHandler handler + ) throws IOException, InterruptedException { + builder.setHeader("User-Agent", USER_AGENT); + builder.timeout(TIMEOUT); + return HTTP_CLIENT.send(builder.build(), handler); + } + + private static String buildUserAgent() { + String env = ModMenu.DEV_ENVIRONMENT ? "/development" : ""; + String loader = "bta-babric"; + + var modMenuVersion = getModMenuVersion(); + var minecraftVersion = "b1.7.3"; + + return "%s/%s (%s/%s%s)".formatted(ModMenu.GITHUB_REF, modMenuVersion, minecraftVersion, loader, env); + } + + private static String getModMenuVersion() { + var container = FabricLoader.getInstance().getModContainer(ModMenu.MOD_ID); + if (container.isEmpty()) { + throw new RuntimeException("Unable to find Modmenu's own mod container!"); + } + + return VersionUtil.removeBuildMetadata(container.get().getMetadata().getVersion().getFriendlyString()); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/util/NullScreenFactory.java b/src/main/java/io/github/prospector/modmenu/util/NullScreenFactory.java new file mode 100644 index 000000000..7f6d0a4a3 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/NullScreenFactory.java @@ -0,0 +1,15 @@ +package io.github.prospector.modmenu.util; + +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import net.minecraft.client.gui.Screen; + +/* + * This class is used to detect mods which do not override the default ModMenuApi.getModConfigScreenFactory() + * while still preserving the guarantee the above API method does not return null. + */ +public class NullScreenFactory implements ConfigScreenFactory { + @Override + public S create(Screen parent) { + return null; + } +} diff --git a/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java b/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java new file mode 100644 index 000000000..443f222f2 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java @@ -0,0 +1,340 @@ +package io.github.prospector.modmenu.util; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.api.UpdateChannel; +import io.github.prospector.modmenu.api.UpdateChecker; +import io.github.prospector.modmenu.api.UpdateInfo; +import io.github.prospector.modmenu.util.mod.Mod; +import io.github.prospector.modmenu.util.mod.ModrinthUpdateInfo; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class UpdateCheckerUtil { + public static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu/Update Checker"); + + private static boolean modrinthApiV2Removed = false; + + private static boolean allowsUpdateChecks(Mod mod) { + return mod.allowsUpdateChecks(); + } + + public static void checkForUpdates() { + LOGGER.info("Checking mod updates..."); + new Thread(UpdateCheckerUtil::checkForUpdates0, "ModMenu/Update Checker Thread").start(); + } + + private static void checkForUpdates0() { + List withoutUpdateChecker = new ArrayList<>(); + Map currentVersions = null; + Map updatedVersions = null; + Map> modHashes; + Future> currentVersionsFuture; + Future> updatedVersionsFuture; + + List updatableMods = ModMenu.MODS.values() + .stream() + .filter(UpdateCheckerUtil::allowsUpdateChecks) + .toList(); + + ExecutorService executor = Executors.newCachedThreadPool(); + + try { + for (Mod mod : updatableMods) { + UpdateChecker updateChecker = mod.getUpdateChecker(); + + if (updateChecker == null) { + withoutUpdateChecker.add(mod); + } else { + executor.submit(() -> { + Thread.currentThread().setName("ModMenu/Update Checker/%s".formatted(mod.getName())); + + var update = updateChecker.checkForUpdates(); + mod.setUpdateInfo(update); + + if (update != null && update.isUpdateAvailable()) { + LOGGER.info("Update available for '{}@{}'", mod.getId(), mod.getVersion()); + } + }); + } + } + + if (modrinthApiV2Removed) { + return; + } + + modHashes = getModHashes(withoutUpdateChecker); + + currentVersionsFuture = executor.submit(() -> getCurrentVersions(modHashes.keySet())); + updatedVersionsFuture = executor.submit(() -> getUpdatedVersions(modHashes.keySet())); + + try { + currentVersions = currentVersionsFuture.get(); + updatedVersions = updatedVersionsFuture.get(); + } catch (ExecutionException e) { + LOGGER.error("Error checking for updates: ", e.getCause() != null ? e.getCause() : e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Error checking for updates: ", e); + } + } finally { + executor.shutdown(); + } + + if (currentVersions == null || updatedVersions == null) { + return; + } + + for (var hash : modHashes.keySet()) { + var date = currentVersions.get(hash); + var data = updatedVersions.get(hash); + + if (date == null || data == null) { + continue; + } + + if (Objects.equals(hash, data.hash)) { + continue; + } + + if (date.compareTo(data.releaseDate) >= 0) { + continue; + } + + for (var mod : modHashes.get(hash)) { + mod.setUpdateInfo(data.asUpdateInfo()); + LOGGER.info("Update available for '{}@{}', (-> {})", + mod.getId(), + mod.getVersion(), + data.versionNumber + ); + } + } + } + + private static Map> getModHashes(Collection mods) { + Map> results = new HashMap<>(); + + for (var mod : mods) { + String modId = mod.getId(); + + try { + String hash = mod.getSha512Hash(); + + if (hash != null) { + LOGGER.debug("Hash for {} is {}", modId, hash); + results.putIfAbsent(hash, new HashSet<>()); + results.get(hash).add(mod); + } + } catch (IOException e) { + LOGGER.error("Error getting mod hash for mod {}: ", modId, e); + } + } + + return results; + } + + public static void triggerV2RemovedToast() { + } + + private static @Nullable Map getCurrentVersions(Collection modHashes) { + String body = ModMenu.GSON.toJson(new CurrentVersionsFromHashes(modHashes)); + + var request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(body)) + .header("Content-Type", "application/json") + .uri(URI.create("https://api.modrinth.com/v2/version_files")); + + try { + var response = HttpUtil.request(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 410) { + modrinthApiV2Removed = true; + LOGGER.warn("Cannot check for updates because Modrinth's API v2 is no longer available."); + } else if (response.statusCode() == 200) { + Map results = new HashMap<>(); + JsonObject data = JsonParser.parseString(response.body()).getAsJsonObject(); + + for (var entry : data.entrySet()) { + Instant date; + var version = entry.getValue().getAsJsonObject(); + + try { + date = Instant.parse(version.get("date_published").getAsString()); + } catch (DateTimeParseException e) { + continue; + } + + results.put(entry.getKey(), date); + } + + return results; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Error checking for versions: ", e); + } catch (IOException | RuntimeException e) { + LOGGER.error("Error checking for versions: ", e); + } + + return null; + } + + public static class CurrentVersionsFromHashes { + public Collection hashes; + public String algorithm = "sha512"; + + public CurrentVersionsFromHashes(Collection hashes) { + this.hashes = hashes; + } + } + + private static UpdateChannel getUpdateChannel(String versionType) { + try { + return UpdateChannel.valueOf(versionType.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException | NullPointerException e) { + return UpdateChannel.RELEASE; + } + } + + private static @Nullable Map getUpdatedVersions(Collection modHashes) { + String mcVer = "b1.7.3"; + List loaders = List.of("bta-babric"); + List updateChannels = getUpdateChannels(); + + String body = ModMenu.GSON.toJson(new LatestVersionsFromHashesBody(modHashes, + loaders, + mcVer, + updateChannels + )); + + LOGGER.debug("Body: {}", body); + var latestVersionsRequest = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(body)) + .header("Content-Type", "application/json") + .uri(URI.create("https://api.modrinth.com/v2/version_files/update")); + + try { + var latestVersionsResponse = HttpUtil.request(latestVersionsRequest, HttpResponse.BodyHandlers.ofString()); + + int status = latestVersionsResponse.statusCode(); + LOGGER.debug("Status: {}", status); + if (status == 410) { + modrinthApiV2Removed = true; + LOGGER.warn("Cannot check for updates because Modrinth's API v2 is no longer available."); + } else if (status == 200) { + Map results = new HashMap<>(); + JsonObject responseObject = JsonParser.parseString(latestVersionsResponse.body()).getAsJsonObject(); + LOGGER.debug(String.valueOf(responseObject)); + + for (var entry : responseObject.entrySet()) { + var versionObj = entry.getValue().getAsJsonObject(); + var projectId = versionObj.get("project_id").getAsString(); + var versionType = versionObj.get("version_type").getAsString(); + var versionNumber = versionObj.get("version_number").getAsString(); + var versionId = versionObj.get("id").getAsString(); + + var filesArray = versionObj.get("files").getAsJsonArray(); + com.google.gson.JsonElement primaryFile = null; + for (var fileElement : filesArray) { + if (fileElement.getAsJsonObject().get("primary").getAsBoolean()) { + primaryFile = fileElement; + break; + } + } + + if (primaryFile == null) { + continue; + } + + Instant date; + try { + date = Instant.parse(versionObj.get("date_published").getAsString()); + } catch (DateTimeParseException e) { + continue; + } + + var updateChannel = UpdateCheckerUtil.getUpdateChannel(versionType); + var versionHash = primaryFile.getAsJsonObject() + .get("hashes") + .getAsJsonObject() + .get("sha512") + .getAsString(); + + results.put(entry.getKey(), + new VersionUpdate(projectId, versionId, versionNumber, date, updateChannel, versionHash) + ); + } + LOGGER.info("Body sent to Modrinth: {}", body); + LOGGER.info("Modrinth response status: {}, body: {}", status, latestVersionsResponse.body()); + + return results; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Error checking for updates: ", e); + } catch (IOException | RuntimeException e) { + LOGGER.error("Error checking for updates: ", e); + } + + return null; + } + + private static List getUpdateChannels() { + return switch(UpdateChannel.getUserPreference()) { + case RELEASE -> List.of(UpdateChannel.RELEASE); + case BETA -> List.of(UpdateChannel.BETA, UpdateChannel.RELEASE); + default -> List.of(UpdateChannel.ALPHA, UpdateChannel.BETA, UpdateChannel.RELEASE); + }; + } + + private record VersionUpdate( + String projectId, + String versionId, + String versionNumber, + Instant releaseDate, + UpdateChannel updateChannel, + String hash + ) { + private UpdateInfo asUpdateInfo() { + return new ModrinthUpdateInfo(this.projectId, this.versionId, this.versionNumber, this.updateChannel); + } + } + + public static class LatestVersionsFromHashesBody { + public Collection hashes; + public String algorithm = "sha512"; + public Collection loaders; + @SerializedName("game_versions") + public Collection gameVersions; + @SerializedName("version_types") + public Collection versionTypes; + + public LatestVersionsFromHashesBody( + Collection hashes, + Collection loaders, + String mcVersion, + Collection updateChannels + ) { + this.hashes = hashes; + this.loaders = loaders; + this.gameVersions = Set.of(mcVersion); + this.versionTypes = updateChannels.stream().map(value -> value.toString().toLowerCase()).toList(); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/util/VersionUtil.java b/src/main/java/io/github/prospector/modmenu/util/VersionUtil.java new file mode 100644 index 000000000..5631db933 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/VersionUtil.java @@ -0,0 +1,30 @@ +package io.github.prospector.modmenu.util; + +import java.util.List; + +public final class VersionUtil { + private static final List PREFIXES = List.of("version", "ver", "v"); + + private VersionUtil() { + } + + public static String stripPrefix(String version) { + version = version.trim(); + + for (String prefix : PREFIXES) { + if (version.startsWith(prefix)) { + return version.substring(prefix.length()); + } + } + + return version; + } + + public static String getPrefixedVersion(String version) { + return "v" + stripPrefix(version); + } + + public static String removeBuildMetadata(String version) { + return version.split("\\+")[0]; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java b/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java new file mode 100644 index 000000000..eb5f2e908 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java @@ -0,0 +1,91 @@ +package io.github.prospector.modmenu.util.mod; + +import io.github.prospector.modmenu.api.UpdateChecker; +import io.github.prospector.modmenu.api.UpdateInfo; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.metadata.ModMetadata; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.List; +import java.util.Optional; + +public class FabricMod implements Mod { + private final ModMetadata metadata; + private UpdateChecker updateChecker; + private UpdateInfo updateInfo; + + public FabricMod(ModContainer container) { + this.metadata = container.getMetadata(); + } + + @Override + public @NotNull String getId() { + return this.metadata.getId(); + } + + @Override + public @NotNull String getName() { + return this.metadata.getName(); + } + + @Override + public @NotNull String getVersion() { + return this.metadata.getVersion().getFriendlyString(); + } + + @Override + public boolean allowsUpdateChecks() { + return true; + } + + @Override + public @Nullable UpdateChecker getUpdateChecker() { + return this.updateChecker; + } + + @Override + public void setUpdateChecker(@Nullable UpdateChecker updateChecker) { + this.updateChecker = updateChecker; + } + + @Override + public @Nullable UpdateInfo getUpdateInfo() { + return this.updateInfo; + } + + @Override + public void setUpdateInfo(@Nullable UpdateInfo updateInfo) { + this.updateInfo = updateInfo; + } + + @Override + public @Nullable String getSha512Hash() throws IOException { + Optional containerOpt = FabricLoader.getInstance().getModContainer(getId()); + if (containerOpt.isPresent()) { + List paths = containerOpt.get().getOrigin().getPaths(); + if (!paths.isEmpty()) { + Path path = paths.get(0); + if (path != null && Files.exists(path) && !Files.isDirectory(path)) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-512"); + byte[] hashBytes = digest.digest(Files.readAllBytes(path)); + StringBuilder sb = new StringBuilder(); + for (byte b : hashBytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (Exception e) { + return null; + } + } + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java b/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java new file mode 100644 index 000000000..6c5dd1796 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java @@ -0,0 +1,30 @@ +package io.github.prospector.modmenu.util.mod; + +import io.github.prospector.modmenu.api.UpdateChecker; +import io.github.prospector.modmenu.api.UpdateInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public interface Mod { + @NotNull String getId(); + + @NotNull String getName(); + + @NotNull String getVersion(); + + boolean allowsUpdateChecks(); + + @Nullable UpdateChecker getUpdateChecker(); + + void setUpdateChecker(@Nullable UpdateChecker updateChecker); + + @Nullable UpdateInfo getUpdateInfo(); + + void setUpdateInfo(@Nullable UpdateInfo updateInfo); + + default @Nullable String getSha512Hash() throws IOException { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java b/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java new file mode 100644 index 000000000..e1989e2f1 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java @@ -0,0 +1,31 @@ +package io.github.prospector.modmenu.util.mod; + +import io.github.prospector.modmenu.api.UpdateChannel; +import io.github.prospector.modmenu.api.UpdateInfo; +import io.github.prospector.modmenu.util.VersionUtil; +import net.minecraft.core.lang.I18n; +import org.jetbrains.annotations.NotNull; + +public record ModrinthUpdateInfo(String projectId, String versionId, String versionNumber, + UpdateChannel getUpdateChannel) implements UpdateInfo { + + @Override + public boolean isUpdateAvailable() { + return true; + } + + @Override + public @NotNull String getUpdateMessage() { + String versionNoPrefix = VersionUtil.stripPrefix(this.versionNumber); + String key = "modmenu.updateText"; + if (!Character.isDigit(versionNoPrefix.charAt(0))) { + key += ".nonNumerical"; + } + return I18n.getInstance().translateKeyAndFormat(key, versionNoPrefix, I18n.getInstance().translateKey("modmenu.modrinth")); + } + + @Override + public String getDownloadLink() { + return "https://modrinth.com/project/%s/version/%s".formatted(projectId, versionId); + } +} \ No newline at end of file diff --git a/src/main/resources/assets/modmenu/lang/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang index 2185f5d67..3fb82b24e 100644 --- a/src/main/resources/assets/modmenu/lang/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US.lang @@ -39,6 +39,13 @@ modmenu.viewCredits=View Credits modmenu.license=License: modmenu.links=Links: modmenu.sources=Source +modmenu.hasUpdate=An update is available: +modmenu.experimental=(Mod Menu update checker is experimental!) +modmenu.childHasUpdate=A child of this mod has an update available. +modmenu.updateText=v%s on %s +modmenu.updateText.nonNumerical=%s on %s +modmenu.install_version=Install version %s +modmenu.downloadLink=Download modmenu.bluesky=Bluesky modmenu.buymeacoffee=Buy Me a Coffee @@ -117,6 +124,16 @@ option.modmenu.translate_names.false=Not Localized option.modmenu.translate_descriptions=Descriptions option.modmenu.translate_descriptions.true=Localized option.modmenu.translate_descriptions.false=Not Localized +option.modmenu.update_checker=Update Checker +option.modmenu.update_checker.true=Enabled +option.modmenu.update_checker.false=Disabled +option.modmenu.button_update_badge=Update Indicator +option.modmenu.button_update_badge.true=Shown +option.modmenu.button_update_badge.false=Hidden option.modmenu.quick_configure=Quick Configure option.modmenu.quick_configure.true=Enabled option.modmenu.quick_configure.false=Disabled +option.modmenu.update_channel=Update Channel +option.modmenu.update_channel.alpha=All +option.modmenu.update_channel.beta=Release & Beta +option.modmenu.update_channel.release=Release diff --git a/src/main/resources/assets/modmenu/lang/uk_UA.lang b/src/main/resources/assets/modmenu/lang/uk_UA.lang index 748881099..ee1192c35 100644 --- a/src/main/resources/assets/modmenu/lang/uk_UA.lang +++ b/src/main/resources/assets/modmenu/lang/uk_UA.lang @@ -39,6 +39,13 @@ modmenu.viewCredits=Показати авторів modmenu.license=Ліцензія: modmenu.links=Посилання: modmenu.sources=Початковий код +modmenu.hasUpdate=Оновлення доступне: +modmenu.experimental=(Перевірка оновлень є експериментальною!) +modmenu.childHasUpdate=Дочірній мод цього мода має доступне оновлення +modmenu.updateText=v%s на %s +modmenu.updateText.nonNumerical=%s на %s +modmenu.install_version=Установити версію %s +modmenu.downloadLink=Завантажити modmenu.bluesky=Bluesky modmenu.buymeacoffee=Buy Me a Coffee @@ -117,6 +124,16 @@ option.modmenu.translate_names.false=Не перекладено option.modmenu.translate_descriptions=Описи option.modmenu.translate_descriptions.true=Перекладено option.modmenu.translate_descriptions.false=Не перекладено +option.modmenu.update_checker=Перевірка оновлень +option.modmenu.update_checker.true=УВІМК. +option.modmenu.update_checker.false=ВИМК. +option.modmenu.button_update_badge=Індикатор онов. +option.modmenu.button_update_badge.true=Показано +option.modmenu.button_update_badge.false=Приховано option.modmenu.quick_configure=Швидке налаштування option.modmenu.quick_configure.true=УВІМК. option.modmenu.quick_configure.false=ВИМК. +option.modmenu.update_channel=Типи оновлень +option.modmenu.update_channel.alpha=Усі +option.modmenu.update_channel.beta=Випуск/бета +option.modmenu.update_channel.release=Випуск diff --git a/src/main/resources/assets/modmenu/textures/gui/update_available.png b/src/main/resources/assets/modmenu/textures/gui/update_available.png new file mode 100644 index 0000000000000000000000000000000000000000..a5c19b6f64e4b794f68241fbef0eb2fc22ba2acf GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^96)Tq!3-p&f1IuaQv3lvA+A9B|NsBt{~zj{-N@@; zS>k$73n;``666=m;PC858jus`>Eaktack}cN3O#TJkEi&ukZgq)0%c_$&?Edrf%6V z|Db2cWEaPNmR^I)^SZZOb?LwJx?bi-*^Y;2iqafDpL@DS=}q$Vb9SL7+l)7yGF~pf o+*bUy^f|eOQV*~1{PCHa{{r(Cy*cw&18rjPboFyt=akR{0O0>k1poj5 literal 0 HcmV?d00001 diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index a18440fe6..db1082c74 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -11,7 +11,7 @@ "io.github.prospector.modmenu.ModMenu" ], "modmenu": [ - "io.github.prospector.modmenu.config.ModMenuConfigIntegration" + "io.github.prospector.modmenu.impl.ModMenuApiImpl" ] }, "contact": { From bf7218d54789b22d39bed9a440465c09adb66a3a Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:38:29 +0300 Subject: [PATCH 51/59] new --- .../io/github/prospector/modmenu/ModMenu.java | 113 +++-- .../modmenu/config/ModMenuConfig.java | 208 ++++++--- .../modmenu/config/ModMenuConfigScreen.java | 398 +++++++++--------- .../prospector/modmenu/gui/ModListScreen.java | 2 +- .../modmenu/mixin/ScreenMainMenuMixin.java | 63 ++- .../modmenu/mixin/ScreenPauseMixin.java | 6 +- .../modmenu/util/UpdateCheckerUtil.java | 8 +- .../modmenu/util/mod/FabricMod.java | 26 +- .../prospector/modmenu/util/mod/Mod.java | 15 + src/main/resources/fabric.mod.json | 2 +- 10 files changed, 532 insertions(+), 309 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 61708b96b..0fb187ba5 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -4,11 +4,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import io.github.prospector.modmenu.api.ModMenuApi; +import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.util.TestModContainer; import io.github.prospector.modmenu.util.UpdateCheckerUtil; import io.github.prospector.modmenu.util.mod.FabricMod; import io.github.prospector.modmenu.util.mod.Mod; -import net.fabricmc.api.ModInitializer; +import net.fabricmc.api.ClientModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.CustomValue; @@ -27,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -public class ModMenu implements ModInitializer { +public class ModMenu implements ClientModInitializer { public static final String MOD_ID = "modmenu"; public static final String GITHUB_REF = "Turnip-Labs/ModMenu"; static { @@ -39,14 +41,20 @@ public class ModMenu implements ModInitializer { private static final Map LEGACY_CONFIG_SCREEN_TASKS = new HashMap<>(); public static final List LIBRARY_MODS = new ArrayList<>(); public static final Set CLIENTSIDE_MODS = new HashSet<>(); - public static final Set DEPRECATED_MODS = new HashSet<>(); + public static final Set DEPRECATED_MODS = new HashSet<>(); public static final Set PATCHWORK_FORGE_MODS = new HashSet<>(); - public static final Map>> CUSTOM_BADGE_MODS = new HashMap<>(); + public static final Map>> CUSTOM_BADGE_MODS = new HashMap<>(); public static final Map> PARENT_MAP = new HashMap<>(); - private static Map> configScreenFactories = new HashMap<>(); + private static final Map> configScreenFactories = new HashMap<>(); public static final Map MODS = new ConcurrentHashMap<>(); public static final boolean DEV_ENVIRONMENT = FabricLoader.getInstance().isDevelopmentEnvironment(); + private static int cachedDisplayedModCount = -1; + + public static void invalidateCache() { + cachedDisplayedModCount = -1; + } + public static boolean hasConfigScreenFactory(String modid) { return configScreenFactories.containsKey(modid); } @@ -61,12 +69,72 @@ public static void openConfigScreen(String modid) { if (opener != null) opener.run(); } - public static String createModsButtonText(boolean showCount) { - String baseText = I18n.getInstance().translateKey("modmenu.title"); - if (showCount) { - return baseText + " " + I18n.getInstance().translateKeyAndFormat("modmenu.loaded", getFormattedModCount()); + public static String getDisplayedModCount() { + if (cachedDisplayedModCount == -1) { + ModMenuConfig config = ModMenuConfigManager.getConfig(); + boolean includeChildren = config.getCountChildren(); + boolean includeLibraries = config.getCountLibraries(); + boolean includeHidden = config.getCountHiddenMods(); + + long count = MODS.values().stream().filter(mod -> { + boolean isChild = mod.getParent() != null; + if (!includeChildren && isChild) { + return false; + } + + boolean isLibrary = ModMenu.LIBRARY_MODS.contains(mod.getId()); + if (!includeLibraries && isLibrary) { + return false; + } + + return includeHidden || !mod.isHidden(); + }).count(); + + if (Boolean.getBoolean("modmenu.debug")) { + count += TestModContainer.getTestModContainers().size(); + } + + cachedDisplayedModCount = Math.toIntExact(count); + } + return NumberFormat.getInstance().format(cachedDisplayedModCount); + } + + public static String createModsButtonText(boolean title) { + ModMenuConfig config = ModMenuConfigManager.getConfig(); + var titleStyle = config.getModsButtonStyle(); + var gameMenuStyle = config.getGameMenuButtonStyle(); + + boolean isIcon = title ? + titleStyle == ModMenuConfig.ModsButtonStyle.ICON : + gameMenuStyle == ModMenuConfig.GameMenuButtonStyle.ICON; + + boolean isShort = title ? + titleStyle == ModMenuConfig.ModsButtonStyle.INSERT : + gameMenuStyle == ModMenuConfig.GameMenuButtonStyle.INSERT; + + I18n i18n = I18n.getInstance(); + String modsText = i18n.translateKey("modmenu.title"); + + if (config.getModCountLocation().isOnModsButton() && !isIcon) { + String count = getDisplayedModCount(); + if (!isShort) { + modsText += " " + i18n.translateKeyAndFormat("modmenu.loaded", count); + } else { + String specificKey = "modmenu.loaded." + count; + String key; + + String nKey = i18n.translateKey(specificKey); + boolean hasSpecific = !nKey.equals(specificKey); + key = hasSpecific ? specificKey : "modmenu.loaded"; + + String secretKey = specificKey + ".secret"; + if (config.getEasterEggs() && !i18n.translateKey(secretKey).equals(secretKey)) { + key = secretKey; + } + modsText += " " + i18n.translateKeyAndFormat(key, count); + } } - return baseText; + return modsText; } public static void addLegacyConfigScreenTask(String modid, Runnable task) { @@ -83,9 +151,8 @@ public static void addLibraryMod(String modid) { LIBRARY_MODS.add(modid); } - @SuppressWarnings("RedundantCollectionOperation") @Override - public void onInitialize() { + public void onInitializeClient() { ModMenuConfigManager.initializeConfig(); FabricLoader loader = FabricLoader.getInstance(); loader.getEntrypointContainers("modmenu", ModMenuApi.class).forEach(entrypoint -> { @@ -98,12 +165,12 @@ public void onInitialize() { configScreenFactories.put(mod.getMetadata().getId(), factory::create); } } catch (NoSuchMethodException ignored) {} - api.attachCustomBadges((name, outlineColor, fillColor) -> { - Map> map = new HashMap<>(); - map.put(name, new AbstractMap.SimpleEntry<>(outlineColor, fillColor)); - CUSTOM_BADGE_MODS.put(mod.getMetadata().getId(), map); - }); - }); + api.attachCustomBadges((name, outlineColor, fillColor) -> { + Map> map = new HashMap<>(); + map.put(name, new AbstractMap.SimpleEntry<>(outlineColor, fillColor)); + CUSTOM_BADGE_MODS.put(mod.getMetadata().getId(), map); + }); + }); configScreenFactories.put("minecraft", (screenBase -> new ScreenOptions(screenBase, OptionsPages.GENERAL))); Collection mods = loader.getAllMods(); for (ModContainer mod : mods) { @@ -128,9 +195,9 @@ public void onInitialize() { } // Deprecated badges - if (metadata.containsCustomValue("modmenu:deprecated") && metadata.getCustomValue("modmenu:deprecated").getAsBoolean()) { - DEPRECATED_MODS.add(id); - } + if (metadata.containsCustomValue("modmenu:deprecated") && metadata.getCustomValue("modmenu:deprecated").getAsBoolean()) { + DEPRECATED_MODS.add(id); + } // Patchwork (unused) if (metadata.containsCustomValue("patchwork:source") && metadata.getCustomValue("patchwork:source").getAsObject() != null) { @@ -169,8 +236,4 @@ public static boolean areModUpdatesAvailable() { } return false; } - - public static String getFormattedModCount() { - return NumberFormat.getInstance().format(FabricLoader.getInstance().getAllMods().size()); - } } diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 4e21303f8..03400032f 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -1,46 +1,73 @@ package io.github.prospector.modmenu.config; +import io.github.prospector.modmenu.ModMenu; import net.fabricmc.loader.api.ModContainer; import net.minecraft.core.lang.I18n; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; public class ModMenuConfig { private Sorting sorting = Sorting.ASCENDING; + private boolean countLibraries = true; + private boolean countChildren = true; + private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; + private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; + private boolean countHiddenMods = true; + private ModCountLocation modCountLocation = ModCountLocation.TITLE_SCREEN; + private boolean hideModLinks = false; private boolean showLibraries = false; - private boolean hideConfigButtons = false; + private boolean hideModLicense = false; private boolean hideBadges = false; - private boolean hideModLinks = false; private boolean hideModCredits = false; - private boolean hideModLicense = false; private boolean easterEggs = true; - private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; - private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; private boolean randomJavaColors = true; private boolean translateNames = true; private boolean translateDescriptions = true; private boolean updateChecker = true; private boolean buttonUpdateBadge = true; - private boolean quickConfigure = true; private UpdateChannel updateChannel = UpdateChannel.RELEASE; + private boolean quickConfigure = true; + private boolean hideConfigButtons = false; + private List hiddenMods = new ArrayList<>(); + private List disableUpdateChecker = new ArrayList<>(); public void toggleSortMode() { this.sorting = next(this.sorting); ModMenuConfigManager.save(); } - public void toggleShowLibraries() { - this.showLibraries = !this.showLibraries; + public void toggleCountLibraries() { + this.countLibraries = !this.countLibraries; + ModMenu.invalidateCache(); ModMenuConfigManager.save(); } - public void toggleHideConfigButtons() { - this.hideConfigButtons = !this.hideConfigButtons; + public void toggleCountChildren() { + this.countChildren = !this.countChildren; + ModMenu.invalidateCache(); ModMenuConfigManager.save(); } - public void toggleHideBadges() { - this.hideBadges = !this.hideBadges; + public void toggleModCountLocation() { + this.modCountLocation = next(this.modCountLocation); + ModMenuConfigManager.save(); + } + + public void toggleGameMenuButtonStyle() { + this.gameMenuButtonStyle = next(this.gameMenuButtonStyle); + ModMenuConfigManager.save(); + } + + public void toggleCountHiddenMods() { + this.countHiddenMods = !this.countHiddenMods; + ModMenu.invalidateCache(); + ModMenuConfigManager.save(); + } + + public void toggleModsButtonStyle() { + this.modsButtonStyle = next(this.modsButtonStyle); ModMenuConfigManager.save(); } @@ -49,8 +76,8 @@ public void toggleHideModLinks() { ModMenuConfigManager.save(); } - public void toggleHideModCredits() { - this.hideModCredits = !this.hideModCredits; + public void toggleShowLibraries() { + this.showLibraries = !this.showLibraries; ModMenuConfigManager.save(); } @@ -59,18 +86,18 @@ public void toggleHideModLicense() { ModMenuConfigManager.save(); } - public void toggleEasterEggs() { - this.easterEggs = !this.easterEggs; + public void toggleHideBadges() { + this.hideBadges = !this.hideBadges; ModMenuConfigManager.save(); } - public void toggleGameMenuButtonStyle() { - this.gameMenuButtonStyle = next(this.gameMenuButtonStyle); + public void toggleHideModCredits() { + this.hideModCredits = !this.hideModCredits; ModMenuConfigManager.save(); } - public void toggleModsButtonStyle() { - this.modsButtonStyle = next(this.modsButtonStyle); + public void toggleEasterEggs() { + this.easterEggs = !this.easterEggs; ModMenuConfigManager.save(); } @@ -99,13 +126,18 @@ public void toggleButtonUpdateBadge() { ModMenuConfigManager.save(); } + public void toggleUpdateChannel() { + this.updateChannel = next(this.updateChannel); + ModMenuConfigManager.save(); + } + public void toggleQuickConfigure() { this.quickConfigure = !this.quickConfigure; ModMenuConfigManager.save(); } - public void toggleUpdateChannel() { - this.updateChannel = next(this.updateChannel); + public void toggleHideConfigButtons() { + this.hideConfigButtons = !this.hideConfigButtons; ModMenuConfigManager.save(); } @@ -113,40 +145,52 @@ public Sorting getSorting() { return sorting; } - public boolean getShowLibraries() { - return showLibraries; + public boolean getCountLibraries() { + return countLibraries; } - public boolean getHideConfigButtons() { - return hideConfigButtons; + public boolean getCountChildren() { + return countChildren; } - public boolean getHideBadges() { - return hideBadges; + public ModCountLocation getModCountLocation() { + return modCountLocation; + } + + public GameMenuButtonStyle getGameMenuButtonStyle() { + return gameMenuButtonStyle; + } + + public boolean getCountHiddenMods() { + return countHiddenMods; + } + + public ModsButtonStyle getModsButtonStyle() { + return modsButtonStyle; } public boolean getHideModLinks() { return hideModLinks; } - public boolean getHideModCredits() { - return hideModCredits; + public boolean getShowLibraries() { + return showLibraries; } public boolean getHideModLicense() { return hideModLicense; } - public boolean getEasterEggs() { - return easterEggs; + public boolean getHideBadges() { + return hideBadges; } - public GameMenuButtonStyle getGameMenuButtonStyle() { - return gameMenuButtonStyle; + public boolean getHideModCredits() { + return hideModCredits; } - public ModsButtonStyle getModsButtonStyle() { - return modsButtonStyle; + public boolean getEasterEggs() { + return easterEggs; } public boolean getRandomJavaColors() { @@ -169,52 +213,76 @@ public boolean getButtonUpdateBadge() { return buttonUpdateBadge; } + public UpdateChannel getUpdateChannel() { + return updateChannel; + } + public boolean getQuickConfigure() { return quickConfigure; } - public UpdateChannel getUpdateChannel() { - return updateChannel; + public boolean getHideConfigButtons() { + return hideConfigButtons; + } + + public List getHiddenMods() { + return hiddenMods; + } + + public List getDisableUpdateChecker() { + return disableUpdateChecker; } public String getSortingDisplayString() { return optionString("sorting", this.sorting.getName()); } - public String getShowLibrariesDisplayString() { - return optionString("show_libraries", I18n.getInstance().translateKey("option.modmenu.show_libraries." + this.showLibraries)); + public String getCountLibrariesDisplayString() { + return optionString("count_libraries", I18n.getInstance().translateKey("option.modmenu.count_libraries." + this.countLibraries)); } - public String getHideConfigButtonsDisplayString() { - return optionString("hide_config_buttons", I18n.getInstance().translateKey("option.modmenu.hide_config_buttons." + this.hideConfigButtons)); + public String getCountChildrenDisplayString() { + return optionString("count_children", I18n.getInstance().translateKey("option.modmenu.count_children." + this.countChildren)); } - public String getHideBadgesDisplayString() { - return optionString("hide_badges", I18n.getInstance().translateKey("option.modmenu.hide_badges." + this.hideBadges)); + public String getModCountLocationDisplayString() { + return optionString("mod_count_location", this.modCountLocation.getName()); + } + + public String getGameMenuButtonStyleDisplayString() { + return optionString("game_menu_button_style", this.gameMenuButtonStyle.getName()); + } + + public String getCountHiddenModsDisplayString() { + return optionString("count_hidden_mods", I18n.getInstance().translateKey("option.modmenu.count_hidden_mods." + this.countHiddenMods)); + } + + public String getModsButtonStyleDisplayString() { + return optionString("mods_button_style", this.modsButtonStyle.getName()); } public String getHideModLinksDisplayString() { return optionString("hide_mod_links", I18n.getInstance().translateKey("option.modmenu.hide_mod_links." + this.hideModLinks)); } - public String getHideModCreditsDisplayString() { - return optionString("hide_mod_credits", I18n.getInstance().translateKey("option.modmenu.hide_mod_credits." + this.hideModCredits)); + public String getShowLibrariesDisplayString() { + return optionString("show_libraries", I18n.getInstance().translateKey("option.modmenu.show_libraries." + this.showLibraries)); } public String getHideModLicenseDisplayString() { - return optionString("hide_mod_license", I18n.getInstance().translateKey("option.modmenu.hide_mod_license." + this.getHideModLicense())); + return optionString("hide_mod_license", I18n.getInstance().translateKey("option.modmenu.hide_mod_license." + this.hideModLicense)); } - public String getEasterEggsDisplayString() { - return optionString("easter_eggs", I18n.getInstance().translateKey("option.modmenu.easter_eggs." + this.easterEggs)); + public String getHideBadgesDisplayString() { + return optionString("hide_badges", I18n.getInstance().translateKey("option.modmenu.hide_badges." + this.hideBadges)); } - public String getModsButtonStyleDisplayString() { - return optionString("mods_button_style", this.modsButtonStyle.getName()); + public String getHideModCreditsDisplayString() { + return optionString("hide_mod_credits", I18n.getInstance().translateKey("option.modmenu.hide_mod_credits." + this.hideModCredits)); } - public String getGameMenuButtonStyleDisplayString() { - return optionString("game_menu_button_style", this.gameMenuButtonStyle.getName()); + public String getEasterEggsDisplayString() { + return optionString("easter_eggs", I18n.getInstance().translateKey("option.modmenu.easter_eggs." + this.easterEggs)); } public String getRandomJavaColorsDisplayString() { @@ -237,14 +305,14 @@ public String getButtonUpdateBadgeDisplayString() { return optionString("button_update_badge", I18n.getInstance().translateKey("option.modmenu.button_update_badge." + this.buttonUpdateBadge)); } - public String getQuickConfigureDisplayString() { - return optionString("quick_configure", I18n.getInstance().translateKey("option.modmenu.quick_configure." + this.quickConfigure)); - } - public String getUpdateChannelDisplayString() { return optionString("update_channel", this.updateChannel.getName()); } + public String getQuickConfigureDisplayString() { + return optionString("quick_configure", I18n.getInstance().translateKey("option.modmenu.quick_configure." + this.quickConfigure)); + } + private static > T next(T current) { T[] values = current.getDeclaringClass().getEnumConstants(); return values[(current.ordinal() + 1) % values.length]; @@ -283,6 +351,32 @@ public String getKey() { } } + public enum ModCountLocation implements OptionEnum { + TITLE_SCREEN("option.modmenu.mod_count_location.title_screen"), + MODS_BUTTON("option.modmenu.mod_count_location.mods_button"), + TITLE_SCREEN_AND_MODS_BUTTON("option.modmenu.mod_count_location.title_screen_and_mods_button"), + NONE("option.modmenu.mod_count_location.none"); + + final String key; + + ModCountLocation(String key) { + this.key = key; + } + + public boolean isOnModsButton() { + return this == MODS_BUTTON || this == TITLE_SCREEN_AND_MODS_BUTTON; + } + + public boolean isOnTitleScreen() { + return this == TITLE_SCREEN_AND_MODS_BUTTON || this == TITLE_SCREEN; + } + + @Override + public String getKey() { + return key; + } + } + public enum ModsButtonStyle implements OptionEnum { INSERT("option.modmenu.mods_button_style.insert"), ICON("option.modmenu.mods_button_style.icon"); diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java index 8e4d5c005..c99357fb7 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -1,13 +1,24 @@ package io.github.prospector.modmenu.config; import io.github.prospector.modmenu.gui.ModListScreen; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; +import net.minecraft.client.render.renderer.GLRenderer; +import net.minecraft.client.render.renderer.Shaders; +import net.minecraft.client.render.tessellator.TessellatorGeneral; import net.minecraft.core.lang.I18n; +import net.minecraft.core.sound.SoundCategory; import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; public class ModMenuConfigScreen extends Screen { private final Screen parent; + private float scrollPosition = 0.0F; + private int contentHeight = 0; + private boolean scrolling = false; + ModMenuConfig config = ModMenuConfigManager.getConfig(); public ModMenuConfigScreen(Screen parent) { super(parent); @@ -16,223 +27,210 @@ public ModMenuConfigScreen(Screen parent) { @Override public void init() { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - - int buttonWidth = 150; - int buttonHeight = 20; - int spacing = 10; - int startY = this.height / 6; - - int totalWidth = (buttonWidth * 2) + spacing; - int leftX = this.width / 2 - totalWidth / 2; - int rightX = leftX + buttonWidth + spacing; - - this.add(new ButtonElement( - 100, - leftX, - startY, - buttonWidth, - buttonHeight, - config.getSortingDisplayString() - )); - - this.add(new ButtonElement( - 101, - rightX, - startY, - buttonWidth, - buttonHeight, - config.getShowLibrariesDisplayString() - )); - - this.add(new ButtonElement( - 102, - leftX, - startY + 24, - buttonWidth, - buttonHeight, - config.getModsButtonStyleDisplayString() - )); - - this.add(new ButtonElement( - 103, - rightX, - startY + 24, - buttonWidth, - buttonHeight, - config.getGameMenuButtonStyleDisplayString() - )); - - this.add(new ButtonElement( - 104, - leftX, - startY + 48, - buttonWidth, - buttonHeight, - config.getHideModLinksDisplayString() - )); - - this.add(new ButtonElement( - 105, - rightX, - startY + 48, - buttonWidth, - buttonHeight, - config.getHideModLicenseDisplayString() - )); - - this.add(new ButtonElement( - 106, - leftX, - startY + 72, - buttonWidth, - buttonHeight, - config.getHideBadgesDisplayString() - )); - - this.add(new ButtonElement( - 107, - rightX, - startY + 72, - buttonWidth, - buttonHeight, - config.getHideModCreditsDisplayString() - )); - - this.add(new ButtonElement( - 109, - leftX, - startY + 96, - buttonWidth, - buttonHeight, - config.getEasterEggsDisplayString() - )); - - this.add(new ButtonElement( - 108, - rightX, - startY + 96, - buttonWidth, - buttonHeight, - config.getRandomJavaColorsDisplayString() - )); - - this.add(new ButtonElement( - 110, - leftX, - startY + 120, - buttonWidth, - buttonHeight, - config.getTranslateNamesDisplayString() - )); - - this.add(new ButtonElement( - 111, - rightX, - startY + 120, - buttonWidth, - buttonHeight, - config.getTranslateDescriptionsDisplayString() - )); - - this.add(new ButtonElement( - 112, - leftX, - startY + 144, - buttonWidth, - buttonHeight, - config.getUpdateCheckerDisplayString() - )); - - this.add(new ButtonElement( - 113, - rightX, - startY + 144, - buttonWidth, - buttonHeight, - config.getButtonUpdateBadgeDisplayString() - )); - - this.add(new ButtonElement( - 114, - leftX, - startY + 168, - buttonWidth, - buttonHeight, - config.getQuickConfigureDisplayString() - )); - - this.add(new ButtonElement( - 115, - rightX, - startY + 168, - buttonWidth, - buttonHeight, - config.getUpdateChannelDisplayString() - )); - - - int doneButtonWidth = 200; - this.add(new ButtonElement( - 200, - this.width / 2 - doneButtonWidth / 2, - this.height - 28, - doneButtonWidth, - buttonHeight, - I18n.getInstance().translateKey("gui.options.button.done") - )); + this.buttons.clear(); + + int y = 40; + y = addRow(100, config.getSortingDisplayString(), 101, config.getCountLibrariesDisplayString(), y); + y = addRow(102, config.getCountChildrenDisplayString(), 103, config.getModsButtonStyleDisplayString(), y); + y = addRow(104, config.getGameMenuButtonStyleDisplayString(), 105, config.getCountHiddenModsDisplayString(), y); + y = addRow(106, config.getModCountLocationDisplayString(), 107, config.getHideModLinksDisplayString(), y); + y = addRow(108, config.getShowLibrariesDisplayString(), 109, config.getHideModLicenseDisplayString(), y); + y = addRow(110, config.getHideBadgesDisplayString(), 111, config.getHideModCreditsDisplayString(), y); + y = addRow(112, config.getRandomJavaColorsDisplayString(), 113, config.getEasterEggsDisplayString(), y); + y = addRow(114, config.getTranslateNamesDisplayString(), 115, config.getTranslateDescriptionsDisplayString(), y); + y = addRow(116, config.getUpdateCheckerDisplayString(), 117, config.getButtonUpdateBadgeDisplayString(), y); + y = addRow(118, config.getQuickConfigureDisplayString(), 119, config.getUpdateChannelDisplayString(), y); + + contentHeight = y + 6; + this.add(new ButtonElement(200, (this.width - 200) / 2, this.height - 32, 200, 20, I18n.getInstance().translateKey("gui.options.button.done"))); + } + + private int addRow(int id1, String t1, int y) { + int w = 150, spacing = 10; + int leftX = (this.width - (w * 2 + spacing)) / 2 - 12; + this.buttons.add(createButton(id1, leftX, y, w, t1)); + return y + 24; + } + + private int addRow(int id1, String t1, int id2, String t2, int y) { + int w = 150, spacing = 10; + int leftX = (this.width - (w * 2 + spacing)) / 2 - 12; + this.buttons.add(createButton(id1, leftX, y, w, t1)); + this.buttons.add(createButton(id2, leftX + w + spacing, y, w, t2)); + return y + 24; + } + + private ButtonElement createButton(int id, int x, int y, int w, String text) { + return new ButtonElement(id, x, y, w, 20, text) { + @Override + public void drawButton(Minecraft mc, int mx, int my) { + int ay = this.yPosition - (int) ModMenuConfigScreen.this.scrollPosition; + if (ay + this.height > 32 && ay < ModMenuConfigScreen.this.height - 40) { + boolean hovered = mx >= this.xPosition && my >= ay && mx < this.xPosition + this.width && my < ay + this.height; + boolean inBounds = my >= 32 && my <= ModMenuConfigScreen.this.height - 40; + int oy = this.yPosition; + this.yPosition = ay; + super.drawButton(mc, (hovered && !inBounds) ? -9999 : mx, (hovered && !inBounds) ? -9999 : my); + this.yPosition = oy; + } + } + }; } @Override - public void render(int mouseX, int mouseY, float delta) { - this.renderBackground(); + public void mouseClicked(int mx, int my, int buttonNum) { + int top = 32, bottom = this.height - 40, h = bottom - top; + int maxScroll = Math.max(0, contentHeight - h); + int sbX = (this.width + 310) / 2 + 6; + + if (maxScroll > 0 && buttonNum == 0 && mx >= sbX && mx <= sbX + 6 && my >= top && my <= bottom) { + this.scrolling = true; + return; + } - this.drawStringCenteredNoShadow(this.fontRenderer, I18n.getInstance().translateKey("modmenu.options"), this.width / 2, 20, 0xFFFFFF); + for (Object obj : this.buttons) { + if (obj instanceof ButtonElement b) { + if (b.id == 200) { + if (mx >= b.xPosition && my >= b.yPosition && mx < b.xPosition + b.width && my < b.yPosition + b.height) { + this.clickedButton = b; + if (b.playSound) this.mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); + buttonClicked(b); + return; + } + } else if (my >= top && my <= bottom) { + int ay = b.yPosition - (int) this.scrollPosition; + if (mx >= b.xPosition && my >= ay && mx < b.xPosition + b.width && my < ay + b.height) { + int oy = b.yPosition; + b.yPosition = ay; + boolean clicked = b.mouseClicked(this.mc, mx, my); + b.yPosition = oy; + if (clicked) { + this.clickedButton = b; + if (b.playSound) this.mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); + buttonClicked(b); + return; + } + } + } + } + } + super.mouseClicked(mx, my, buttonNum); + } - super.render(mouseX, mouseY, delta); + @Override + public void mouseReleased(int mx, int my, int buttonNum) { + if (buttonNum == 0) this.scrolling = false; + super.mouseReleased(mx, my, buttonNum); } @Override - protected void buttonClicked(ButtonElement button) { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - String displayString = switch (button.id) { - case 100 -> { config.toggleSortMode(); yield config.getSortingDisplayString(); } - case 101 -> { config.toggleShowLibraries(); yield config.getShowLibrariesDisplayString(); } - case 102 -> { config.toggleModsButtonStyle(); yield config.getModsButtonStyleDisplayString(); } - case 103 -> { config.toggleGameMenuButtonStyle(); yield config.getGameMenuButtonStyleDisplayString(); } - case 104 -> { config.toggleHideModLinks(); yield config.getHideModLinksDisplayString(); } - case 105 -> { config.toggleHideModLicense(); yield config.getHideModLicenseDisplayString(); } - case 106 -> { config.toggleHideBadges(); yield config.getHideBadgesDisplayString(); } - case 107 -> { config.toggleHideModCredits(); yield config.getHideModCreditsDisplayString(); } - case 108 -> { config.toggleEasterEggs(); yield config.getEasterEggsDisplayString(); } - case 109 -> { config.toggleRandomJavaColors(); yield config.getRandomJavaColorsDisplayString(); } - case 110 -> { config.toggleTranslateNames(); yield config.getTranslateNamesDisplayString(); } - case 111 -> { config.toggleTranslateDescriptions(); yield config.getTranslateDescriptionsDisplayString(); } - case 112 -> { config.toggleUpdateChecker(); yield config.getUpdateCheckerDisplayString(); } - case 113 -> { config.toggleButtonUpdateBadge(); yield config.getButtonUpdateBadgeDisplayString(); } - case 114 -> { config.toggleQuickConfigure(); yield config.getQuickConfigureDisplayString(); } - case 115 -> { config.toggleUpdateChannel(); yield config.getUpdateChannelDisplayString(); } - - case 200 -> { this.closeAndReturn(); yield null; } - default -> null; - }; + public void render(int mx, int my, float delta) { + int top = 32, bottom = this.height - 40, h = bottom - top; + int maxScroll = Math.max(0, contentHeight - h); + + if (this.scrolling && maxScroll > 0) { + if (!Mouse.isButtonDown(0)) { + this.scrolling = false; + } else { + float th = Math.max(32, (h * h) / contentHeight); + float track = h - th; + if (track > 0) { + this.scrollPosition = Math.max(0, Math.min((my - top - th / 2) * maxScroll / track, maxScroll)); + } + } + } - if (displayString != null) { - button.displayString = displayString; + int wheel = Mouse.getDWheel(); + if (wheel != 0 && maxScroll > 0) { + this.scrollPosition = Math.max(0, Math.min(this.scrollPosition + (wheel > 0 ? -24 : 24), maxScroll)); + } + + this.renderBackground(); + + int scale = this.mc.resolution.getScale(); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(0, 40 * scale, this.mc.gameWindow.getWidthPixels(), h * scale); + + for (Object obj : this.buttons) { + if (obj instanceof ButtonElement b && b.id != 200) b.drawButton(this.mc, mx, my); + } + + GL11.glDisable(GL11.GL_SCISSOR_TEST); + + this.drawRect(0, 0, this.width, 32, 0x40000000); + this.drawStringCenteredNoShadow(this.fontRenderer, I18n.getInstance().translateKey("modmenu.options"), this.width / 2, 12, 0xFFFFFF); + this.drawRect(0, bottom, this.width, this.height, 0x40000000); + + for (Object obj : this.buttons) { + if (obj instanceof ButtonElement b && b.id == 200) b.drawButton(this.mc, mx, my); + } + + if (maxScroll > 0) { + int k = (this.width + 310) / 2 + 6, l = k + 6; + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.COLOR); + TessellatorGeneral t = GLRenderer.getTessellator(); + int th = Math.max(32, (h * h) / contentHeight); + int r = Math.max(top, (int)this.scrollPosition * (h - th) / maxScroll + top); + + t.startDrawingQuads(); + t.setColorOpaque3i(0, 0, 0); + t.addVertexWithUV(k, bottom, 0, 0, 1); t.addVertexWithUV(l, bottom, 0, 1, 1); + t.addVertexWithUV(l, top, 0, 1, 0); t.addVertexWithUV(k, top, 0, 0, 0); + t.draw(); + + t.startDrawingQuads(); + t.setColorOpaque3i(128, 128, 128); + t.addVertexWithUV(k, r + th, 0, 0, 1); t.addVertexWithUV(l, r + th, 0, 1, 1); + t.addVertexWithUV(l, r, 0, 1, 0); t.addVertexWithUV(k, r, 0, 0, 0); + t.draw(); + + t.startDrawingQuads(); + t.setColorOpaque3i(192, 192, 192); + t.addVertexWithUV(k, r + th - 1, 0, 0, 1); t.addVertexWithUV(l - 1, r + th - 1, 0, 1, 1); + t.addVertexWithUV(l - 1, r, 0, 1, 0); t.addVertexWithUV(k, r, 0, 0, 0); + t.draw(); + GLRenderer.popFrame(); } } @Override - public void keyPressed(char eventCharacter, int eventKey, int mx, int my) { - if (eventKey == Keyboard.KEY_ESCAPE || eventKey == Keyboard.KEY_BACK) { - this.closeAndReturn(); + protected void buttonClicked(ButtonElement button) { + String string = null; + switch (button.id) { + case 100: config.toggleSortMode(); string = config.getSortingDisplayString(); break; + case 101: config.toggleCountLibraries(); string = config.getCountLibrariesDisplayString(); break; + case 102: config.toggleCountChildren(); string = config.getCountChildrenDisplayString(); break; + case 103: config.toggleModsButtonStyle(); string = config.getModsButtonStyleDisplayString(); break; + case 104: config.toggleGameMenuButtonStyle(); string = config.getGameMenuButtonStyleDisplayString(); break; + case 105: config.toggleCountHiddenMods(); string = config.getCountHiddenModsDisplayString(); break; + case 106: config.toggleModCountLocation(); string = config.getModCountLocationDisplayString(); break; + case 107: config.toggleHideModLinks(); string = config.getHideModLinksDisplayString(); break; + case 108: config.toggleShowLibraries(); string = config.getShowLibrariesDisplayString(); break; + case 109: config.toggleHideModLicense(); string = config.getHideModLicenseDisplayString(); break; + case 110: config.toggleHideBadges(); string = config.getHideBadgesDisplayString(); break; + case 111: config.toggleHideModCredits(); string = config.getHideModCreditsDisplayString(); break; + case 112: config.toggleRandomJavaColors(); string = config.getRandomJavaColorsDisplayString(); break; + case 113: config.toggleEasterEggs(); string = config.getEasterEggsDisplayString(); break; + case 114: config.toggleTranslateNames(); string = config.getTranslateNamesDisplayString(); break; + case 115: config.toggleTranslateDescriptions(); string = config.getTranslateDescriptionsDisplayString(); break; + case 116: config.toggleUpdateChecker(); string = config.getUpdateCheckerDisplayString(); break; + case 117: config.toggleButtonUpdateBadge(); string = config.getButtonUpdateBadgeDisplayString(); break; + case 118: config.toggleQuickConfigure(); string = config.getQuickConfigureDisplayString(); break; + case 119: config.toggleUpdateChannel(); string = config.getUpdateChannelDisplayString(); break; + case 200: closeAndReturn(); break; } + if (string != null) button.displayString = string; + } + + @Override + public void keyPressed(char c, int key, int mx, int my) { + if (key == Keyboard.KEY_ESCAPE || key == Keyboard.KEY_BACK) + closeAndReturn(); } private void closeAndReturn() { - if (this.parent instanceof ModListScreen) { - ((ModListScreen) this.parent).getModList().reloadFilters(); - } + if (this.parent instanceof ModListScreen p) p.getModList().reloadFilters(); this.mc.displayScreen(this.parent); } } diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index e9a29ef2b..48661d885 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -385,7 +385,7 @@ public void render(int mouseX, int mouseY, float delta) { String versionString = metadata.getId().equals("minecraft") ? Global.VERSION : metadata.getVersion().getFriendlyString(); - this.drawStringNoShadow(font, "v" + versionString, x + imageOffset, paneY + 2 + lineSpacing, 0x808080); + this.drawStringNoShadow(font, VersionUtil.getPrefixedVersion(versionString), x + imageOffset, paneY + 2 + lineSpacing, 0x808080); String authors; List names = new ArrayList<>(); diff --git a/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java index 529478143..e4cca8b93 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java @@ -6,27 +6,43 @@ import io.github.prospector.modmenu.gui.ModListScreen; import io.github.prospector.modmenu.gui.ModMenuButtonWidget; import io.github.prospector.modmenu.gui.ModMenuTexturedButtonWidget; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; -import net.minecraft.client.gui.Screen; -import net.minecraft.client.gui.ScreenPause; +import net.minecraft.client.gui.ScreenMainMenu; +import net.minecraft.client.option.GameSettings; import net.minecraft.core.lang.I18n; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(value = ScreenPause.class, remap = false) -public class ScreenPauseMixin extends Screen { +import java.util.Random; + +@Mixin(value = ScreenMainMenu.class, remap = false) +public class ScreenMainMenuMixin { + @Unique + I18n i18n = I18n.getInstance(); @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - if (ModMenuConfigManager.getConfig().getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { - I18n i18n = I18n.getInstance(); - String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); - this.buttons.add(new ModMenuButtonWidget(1000, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); + ScreenMainMenu menu = (ScreenMainMenu) (Object) this; + ButtonElement texturePackButton = menu.buttons.get(2); + ModMenuConfig config = ModMenuConfigManager.getConfig(); + if (config.getEasterEggs()) { + texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); + } + if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { + int index = menu.buttons.indexOf(texturePackButton); + if (index != -1) { + String buttonText = ModMenu.createModsButtonText(true); + ButtonElementAccessor accessor = (ButtonElementAccessor) texturePackButton; + ModMenuButtonWidget modButton = new ModMenuButtonWidget(1000, texturePackButton.xPosition, texturePackButton.yPosition, accessor.getWidth(), accessor.getHeight(), buttonText); + menu.buttons.set(index, modButton); + } } else { - this.buttons.removeIf(b -> b.id == 10); - this.buttons.add(new ModMenuTexturedButtonWidget( - 1000, this.width / 2 - 124, this.height / 4 + 104, 20, 20, 0, 0, + menu.buttons.removeIf(b -> b.id == 11); + menu.buttons.add(new ModMenuTexturedButtonWidget( + 1000, menu.width / 2 - 124, menu.height / 4 + 132, 20, 20, 0, 0, "/assets/" + ModMenu.MOD_ID + "/textures/gui/open_button.png", 32, 64 ) {}); } @@ -35,7 +51,28 @@ public class ScreenPauseMixin extends Screen { @Inject(method = "buttonClicked", at = @At("HEAD")) private void modmenu$onActionPerformed(ButtonElement button, CallbackInfo ci) { if (button.id == 1000) { - this.mc.displayScreen(new ModListScreen(this)); + Minecraft.getMinecraft().displayScreen(new ModListScreen((ScreenMainMenu) (Object) this)); + } + } + + @Inject(method = "render", at = @At("RETURN")) + private void modmenu$renderModCount(int mx, int my, float partialTick, CallbackInfo ci) { + ModMenuConfig config = ModMenuConfigManager.getConfig(); + if (config.getModCountLocation().isOnTitleScreen()) { + ScreenMainMenu menu = (ScreenMainMenu) (Object) this; + String count = ModMenu.getDisplayedModCount(); + String specificKey = "modmenu.mods." + count; + String nKey = i18n.translateKey(specificKey); + String key = !nKey.equals(specificKey) ? specificKey : "modmenu.mods.n"; + String secretKey = specificKey + ".secret"; + if (config.getEasterEggs() && !i18n.translateKey(secretKey).equals(secretKey)) { + key = secretKey; + } + String versionText = "Better than Adventure! " + Minecraft.VERSION; + String modCountText = " " + i18n.translateKeyAndFormat(key, count); + int versionWidth = menu.fontRenderer.stringWidth(versionText); + int versionTextColor = !(Boolean) GameSettings.ALPHA_MENU.value && menu.mc.mainMenuBackground.isEnabled() ? 16777215 : 5263440; + menu.drawStringShadow(menu.fontRenderer, modCountText, 2 + versionWidth, 2, versionTextColor); } } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java index ddbc9da0b..8e4c0144e 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java @@ -9,7 +9,6 @@ import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; import net.minecraft.client.gui.ScreenPause; -import net.minecraft.core.lang.I18n; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -20,8 +19,7 @@ public class ScreenPauseMixin extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { if (ModMenuConfigManager.getConfig().getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { - I18n i18n = I18n.getInstance(); - String buttonText = i18n.translateKey("modmenu.title") + " " + i18n.translateKeyAndFormat("modmenu.loaded", ModMenu.getFormattedModCount()); + String buttonText = ModMenu.createModsButtonText(false); this.buttons.add(new ModMenuButtonWidget(1000, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); } else { this.buttons.removeIf(b -> b.id == 10); @@ -38,4 +36,4 @@ public class ScreenPauseMixin extends Screen { this.mc.displayScreen(new ModListScreen(this)); } } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java b/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java index 443f222f2..8f6de300f 100644 --- a/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java @@ -149,9 +149,6 @@ private static Map> getModHashes(Collection mods) { return results; } - public static void triggerV2RemovedToast() { - } - private static @Nullable Map getCurrentVersions(Collection modHashes) { String body = ModMenu.GSON.toJson(new CurrentVersionsFromHashes(modHashes)); @@ -280,8 +277,6 @@ private static UpdateChannel getUpdateChannel(String versionType) { new VersionUpdate(projectId, versionId, versionNumber, date, updateChannel, versionHash) ); } - LOGGER.info("Body sent to Modrinth: {}", body); - LOGGER.info("Modrinth response status: {}, body: {}", status, latestVersionsResponse.body()); return results; } @@ -318,7 +313,6 @@ private UpdateInfo asUpdateInfo() { public static class LatestVersionsFromHashesBody { public Collection hashes; - public String algorithm = "sha512"; public Collection loaders; @SerializedName("game_versions") public Collection gameVersions; @@ -337,4 +331,4 @@ public LatestVersionsFromHashesBody( this.versionTypes = updateChannels.stream().map(value -> value.toString().toLowerCase()).toList(); } } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java b/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java index eb5f2e908..f12cdc03e 100644 --- a/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java +++ b/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java @@ -2,6 +2,8 @@ import io.github.prospector.modmenu.api.UpdateChecker; import io.github.prospector.modmenu.api.UpdateInfo; +import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.util.BadgeRenderer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -12,13 +14,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.Set; public class FabricMod implements Mod { private final ModMetadata metadata; private UpdateChecker updateChecker; private UpdateInfo updateInfo; + private final boolean allowsUpdateChecks = true; public FabricMod(ModContainer container) { this.metadata = container.getMetadata(); @@ -39,9 +44,28 @@ public FabricMod(ModContainer container) { return this.metadata.getVersion().getFriendlyString(); } + @Override + public @Nullable String getParent() { + return null; + } + + @Override + public boolean isHidden() { + return ModMenuConfigManager.getConfig().getHiddenMods().contains(this.getId()); + } + + @Override + public @NotNull Set getBadges() { + return Collections.emptySet(); + } + @Override public boolean allowsUpdateChecks() { - return true; + if (ModMenuConfigManager.getConfig().getDisableUpdateChecker().contains(this.getId())) { + return false; + } else { + return this.allowsUpdateChecks; + } } @Override diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java b/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java index 6c5dd1796..c10698077 100644 --- a/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java +++ b/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java @@ -2,10 +2,13 @@ import io.github.prospector.modmenu.api.UpdateChecker; import io.github.prospector.modmenu.api.UpdateInfo; +import io.github.prospector.modmenu.util.BadgeRenderer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.Collections; +import java.util.Set; public interface Mod { @NotNull String getId(); @@ -14,6 +17,18 @@ public interface Mod { @NotNull String getVersion(); + default @Nullable String getParent() { + return null; + } + + default boolean isHidden() { + return false; + } + + default @NotNull Set getBadges() { + return Collections.emptySet(); + } + boolean allowsUpdateChecks(); @Nullable UpdateChecker getUpdateChecker(); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index db1082c74..610a2363b 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -7,7 +7,7 @@ "license": "MIT", "icon": "assets/modmenu/icon.png", "entrypoints": { - "main": [ + "client": [ "io.github.prospector.modmenu.ModMenu" ], "modmenu": [ From 6a2bac87fcc7f278b6c43d44aa2a721cdc324dbb Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:08:08 +0300 Subject: [PATCH 52/59] newest --- .../modmenu/config/ModMenuConfig.java | 14 +++ .../modmenu/config/ModMenuConfigScreen.java | 52 ++++---- .../prospector/modmenu/gui/ModListEntry.java | 112 +++++++++--------- .../prospector/modmenu/gui/ModListScreen.java | 4 +- .../prospector/modmenu/gui/ModListWidget.java | 1 + .../modmenu/gui/entries/ParentEntry.java | 84 ++++++++----- .../resources/assets/modmenu/lang/en_US.lang | 26 +++- .../resources/assets/modmenu/lang/uk_UA.lang | 28 ++++- 8 files changed, 205 insertions(+), 116 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 03400032f..22a5deb17 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -11,6 +11,7 @@ public class ModMenuConfig { private Sorting sorting = Sorting.ASCENDING; private boolean countLibraries = true; + private boolean compactList = true; private boolean countChildren = true; private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; @@ -44,6 +45,11 @@ public void toggleCountLibraries() { ModMenuConfigManager.save(); } + public void toggleCompactList() { + this.compactList = !this.compactList; + ModMenuConfigManager.save(); + } + public void toggleCountChildren() { this.countChildren = !this.countChildren; ModMenu.invalidateCache(); @@ -149,6 +155,10 @@ public boolean getCountLibraries() { return countLibraries; } + public boolean getCompactList() { + return compactList; + } + public boolean getCountChildren() { return countChildren; } @@ -241,6 +251,10 @@ public String getCountLibrariesDisplayString() { return optionString("count_libraries", I18n.getInstance().translateKey("option.modmenu.count_libraries." + this.countLibraries)); } + public String getCompactListDisplayString() { + return optionString("compact_list", I18n.getInstance().translateKey("option.modmenu.compact_list." + this.compactList)); + } + public String getCountChildrenDisplayString() { return optionString("count_children", I18n.getInstance().translateKey("option.modmenu.count_children." + this.countChildren)); } diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java index c99357fb7..be98bced0 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java @@ -31,15 +31,16 @@ public void init() { int y = 40; y = addRow(100, config.getSortingDisplayString(), 101, config.getCountLibrariesDisplayString(), y); - y = addRow(102, config.getCountChildrenDisplayString(), 103, config.getModsButtonStyleDisplayString(), y); - y = addRow(104, config.getGameMenuButtonStyleDisplayString(), 105, config.getCountHiddenModsDisplayString(), y); - y = addRow(106, config.getModCountLocationDisplayString(), 107, config.getHideModLinksDisplayString(), y); - y = addRow(108, config.getShowLibrariesDisplayString(), 109, config.getHideModLicenseDisplayString(), y); - y = addRow(110, config.getHideBadgesDisplayString(), 111, config.getHideModCreditsDisplayString(), y); - y = addRow(112, config.getRandomJavaColorsDisplayString(), 113, config.getEasterEggsDisplayString(), y); - y = addRow(114, config.getTranslateNamesDisplayString(), 115, config.getTranslateDescriptionsDisplayString(), y); - y = addRow(116, config.getUpdateCheckerDisplayString(), 117, config.getButtonUpdateBadgeDisplayString(), y); - y = addRow(118, config.getQuickConfigureDisplayString(), 119, config.getUpdateChannelDisplayString(), y); + y = addRow(102, config.getCompactListDisplayString(), 103, config.getCountChildrenDisplayString(), y); + y = addRow(104, config.getModsButtonStyleDisplayString(), 105, config.getGameMenuButtonStyleDisplayString(), y); + y = addRow(106, config.getCountHiddenModsDisplayString(), 107, config.getModCountLocationDisplayString(), y); + y = addRow(108, config.getHideModLinksDisplayString(), 109, config.getShowLibrariesDisplayString(), y); + y = addRow(110, config.getHideModLicenseDisplayString(), 111, config.getHideBadgesDisplayString(), y); + y = addRow(112, config.getHideModCreditsDisplayString(), 113, config.getEasterEggsDisplayString(), y); + y = addRow(114, config.getRandomJavaColorsDisplayString(), 115, config.getTranslateNamesDisplayString(), y); + y = addRow(116, config.getTranslateDescriptionsDisplayString(), 117, config.getUpdateCheckerDisplayString(), y); + y = addRow(118, config.getButtonUpdateBadgeDisplayString(), 119, config.getUpdateChannelDisplayString(), y); + y = addRow(120, config.getQuickConfigureDisplayString(), y); contentHeight = y + 6; this.add(new ButtonElement(200, (this.width - 200) / 2, this.height - 32, 200, 20, I18n.getInstance().translateKey("gui.options.button.done"))); @@ -200,24 +201,25 @@ protected void buttonClicked(ButtonElement button) { switch (button.id) { case 100: config.toggleSortMode(); string = config.getSortingDisplayString(); break; case 101: config.toggleCountLibraries(); string = config.getCountLibrariesDisplayString(); break; - case 102: config.toggleCountChildren(); string = config.getCountChildrenDisplayString(); break; - case 103: config.toggleModsButtonStyle(); string = config.getModsButtonStyleDisplayString(); break; - case 104: config.toggleGameMenuButtonStyle(); string = config.getGameMenuButtonStyleDisplayString(); break; - case 105: config.toggleCountHiddenMods(); string = config.getCountHiddenModsDisplayString(); break; - case 106: config.toggleModCountLocation(); string = config.getModCountLocationDisplayString(); break; - case 107: config.toggleHideModLinks(); string = config.getHideModLinksDisplayString(); break; - case 108: config.toggleShowLibraries(); string = config.getShowLibrariesDisplayString(); break; - case 109: config.toggleHideModLicense(); string = config.getHideModLicenseDisplayString(); break; - case 110: config.toggleHideBadges(); string = config.getHideBadgesDisplayString(); break; - case 111: config.toggleHideModCredits(); string = config.getHideModCreditsDisplayString(); break; - case 112: config.toggleRandomJavaColors(); string = config.getRandomJavaColorsDisplayString(); break; + case 102: config.toggleCompactList(); string = config.getCompactListDisplayString(); break; + case 103: config.toggleCountChildren(); string = config.getCountChildrenDisplayString(); break; + case 104: config.toggleModsButtonStyle(); string = config.getModsButtonStyleDisplayString(); break; + case 105: config.toggleGameMenuButtonStyle(); string = config.getGameMenuButtonStyleDisplayString(); break; + case 106: config.toggleCountHiddenMods(); string = config.getCountHiddenModsDisplayString(); break; + case 107: config.toggleModCountLocation(); string = config.getModCountLocationDisplayString(); break; + case 108: config.toggleHideModLinks(); string = config.getHideModLinksDisplayString(); break; + case 109: config.toggleShowLibraries(); string = config.getShowLibrariesDisplayString(); break; + case 110: config.toggleHideModLicense(); string = config.getHideModLicenseDisplayString(); break; + case 111: config.toggleHideBadges(); string = config.getHideBadgesDisplayString(); break; + case 112: config.toggleHideModCredits(); string = config.getHideModCreditsDisplayString(); break; case 113: config.toggleEasterEggs(); string = config.getEasterEggsDisplayString(); break; - case 114: config.toggleTranslateNames(); string = config.getTranslateNamesDisplayString(); break; - case 115: config.toggleTranslateDescriptions(); string = config.getTranslateDescriptionsDisplayString(); break; - case 116: config.toggleUpdateChecker(); string = config.getUpdateCheckerDisplayString(); break; - case 117: config.toggleButtonUpdateBadge(); string = config.getButtonUpdateBadgeDisplayString(); break; - case 118: config.toggleQuickConfigure(); string = config.getQuickConfigureDisplayString(); break; + case 114: config.toggleRandomJavaColors(); string = config.getRandomJavaColorsDisplayString(); break; + case 115: config.toggleTranslateNames(); string = config.getTranslateNamesDisplayString(); break; + case 116: config.toggleTranslateDescriptions(); string = config.getTranslateDescriptionsDisplayString(); break; + case 117: config.toggleUpdateChecker(); string = config.getUpdateCheckerDisplayString(); break; + case 118: config.toggleButtonUpdateBadge(); string = config.getButtonUpdateBadgeDisplayString(); break; case 119: config.toggleUpdateChannel(); string = config.getUpdateChannelDisplayString(); break; + case 120: config.toggleQuickConfigure(); string = config.getQuickConfigureDisplayString(); break; case 200: closeAndReturn(); break; } if (string != null) button.displayString = string; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index cdaf8a23c..7a9cae272 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -5,9 +5,7 @@ import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.entries.ParentEntry; -import io.github.prospector.modmenu.util.BadgeRenderer; -import io.github.prospector.modmenu.util.DrawingUtil; -import io.github.prospector.modmenu.util.RenderUtils; +import io.github.prospector.modmenu.util.*; import io.github.prospector.modmenu.util.mod.Mod; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; @@ -20,6 +18,7 @@ import net.minecraft.client.render.renderer.State; import net.minecraft.client.render.tessellator.TessellatorGeneral; import net.minecraft.client.render.window.CursorShape; +import net.minecraft.core.Global; import net.minecraft.core.lang.I18n; import org.lwjgl.opengl.GL11; import org.slf4j.Logger; @@ -39,6 +38,8 @@ public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry= x && mouseY >= y && mouseX < x + rowWidth && mouseY < y + iconSize; if (hovered) { @@ -97,9 +98,10 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou client.textureManager.bindTexture(client.textureManager.loadTexture("/assets/modmenu/textures/gui/mod_configuration.png")); - float vMin = hoveringIcon ? 32.0f / 256.0f : 0.0f; - float vMax = hoveringIcon ? 64.0f / 256.0f : 32.0f / 256.0f; - float uMax = 32.0f / 256.0f; + float maxUV = config.getCompactList() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; + float vMin = hoveringIcon ? iconSize / maxUV : 0.0f; + float vMax = hoveringIcon ? (iconSize * 2) / maxUV : iconSize / maxUV; + float uMax = iconSize / maxUV; t.startDrawingQuads(); t.addVertexWithUV(x, y + iconSize, 0, 0.0f, vMax); @@ -115,6 +117,8 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou } } + FontRenderer font = this.fontRenderer; + String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; @@ -124,67 +128,56 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou name = metadata.getName(); } String trimmedName = name; - int maxNameWidth = rowWidth - 32 - 3; - FontRenderer font = this.fontRenderer; - trimmedName = ModListScreen.getString(fontRenderer, name, trimmedName, maxNameWidth); - this.drawStringNoShadow(font, trimmedName, x + 32 + 3, y + 1, 0xFFFFFF); - - String descKey = "modmenu.descriptionTranslation." + id; - String translatedDesc = i18n.translateKey(descKey); - String description; - if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { - description = translatedDesc; - } else { - description = metadata.getDescription(); - } + int maxNameWidth = rowWidth - iconSize - 3; + trimmedName = ModListScreen.getString(font, name, trimmedName, maxNameWidth); + this.drawStringNoShadow(font, trimmedName, x + iconSize + 3, y + 1, 0xFFFFFF); - if ("java".equals(id)) { - String vendor = System.getProperty("java.vendor", "Unknown Vendor"); - String javaDist = i18n.translateKeyAndFormat("modmenu.javaDistributionName", vendor); - description = (description != null ? description : "") + "\n" + javaDist; - } - - int currentX = x + 32 + 3 + font.stringWidth(trimmedName) + 2; + int badgeX = x + iconSize + 3 + font.stringWidth(trimmedName) + 2; + int updateBadgeOffset = 0; if (config.getUpdateChecker() && config.getButtonUpdateBadge()) { Mod modInstance = ModMenu.MODS.get(id); if (modInstance != null) { UpdateInfo updateInfo = modInstance.getUpdateInfo(); if (updateInfo != null && updateInfo.isUpdateAvailable()) { - UpdateAvailableBadge.renderBadge(currentX, y + 1); - currentX += 12; + UpdateAvailableBadge.renderBadge(badgeX, y); + updateBadgeOffset = 11; } } - } - - new BadgeRenderer(client, currentX, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); - - if (description != null) { - int textX = x + 32 + 3 + 4; - int textY = y + 9 + 2; - int maxWidth = rowWidth - 32 - 7; - int maxLines = 2; - int currentLine = 0; - + } - String[] paragraphs = description.split("\r?\n"); + new BadgeRenderer(client, badgeX + updateBadgeOffset, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); - for (String paragraph : paragraphs) { - if (currentLine >= maxLines) break; + int lineHeight = 9; + if (config.getCompactList()) { + String versionString = "minecraft".equals(id) ? Global.VERSION : metadata.getVersion().getFriendlyString(); + String prefixedVersion = VersionUtil.getPrefixedVersion(versionString); + RenderUtils.INSTANCE.drawWrappedString(this, prefixedVersion, x + iconSize + 3, y + lineHeight + 2, rowWidth - iconSize - 7, 2, 0x808080); + } else { + String descKey = "modmenu.descriptionTranslation." + id; + String translatedDesc = i18n.translateKey(descKey); + String description; + if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { + description = translatedDesc; + } else { + description = metadata.getDescription(); + } - for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(font, paragraph, maxWidth)) { - if (currentLine >= maxLines) break; + if ("java".equals(id)) { + String vendor = System.getProperty("java.vendor", "Unknown Vendor"); + String javaDist = i18n.translateKeyAndFormat("modmenu.javaDistributionName", vendor); + description = (description != null ? description : "") + "\n" + javaDist; + } - this.drawStringNoShadow(font, line, textX, textY + (currentLine * 9), 0x808080); - currentLine++; - } + if (description != null) { + RenderUtils.INSTANCE.drawWrappedString(this, description, x + iconSize + 3 + 4, y + lineHeight + 2, rowWidth - iconSize - 7, 2, 0x808080); } } GLRenderer.popFrame(); } - static void internalRender(int y, int x) { + static void internalRender(int y, int x, int iconSize) { GLRenderer.pushFrame(); GLRenderer.setShader(Shaders.INTERFACE); GLRenderer.enableState(State.BLEND); @@ -192,9 +185,9 @@ static void internalRender(int y, int x) { TessellatorGeneral t = GLRenderer.getTessellator(); t.startDrawingQuads(); t.addVertexWithUV(x, y, 0, 0, 0); - t.addVertexWithUV(x, y + 32, 0, 0, 1); - t.addVertexWithUV(x + 32, y + 32, 0, 1, 1); - t.addVertexWithUV(x + 32, y, 0, 1, 0); + t.addVertexWithUV(x, y + iconSize, 0, 0, 1); + t.addVertexWithUV(x + iconSize, y + iconSize, 0, 1, 1); + t.addVertexWithUV(x + iconSize, y, 0, 1, 0); t.draw(); GLRenderer.popFrame(); @@ -236,8 +229,8 @@ private BufferedImage createIcon() { LOGGER.error("Invalid icon for mod {}", this.container.getMetadata().getName(), t); return null; } - return null; - } + return null; + } @Override public void mouseClicked(int v, int v1, int i) { @@ -246,8 +239,9 @@ public void mouseClicked(int v, int v1, int i) { if (i == 0 && !(this instanceof ParentEntry)) { String id = metadata.getId(); if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id) && config.getQuickConfigure()) { + int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; int iconLeft = getXOffset(); - if (v >= iconLeft && v < iconLeft + 32 && v1 >= 0 && v1 < 32) { + if (v >= iconLeft && v < iconLeft + iconSize && v1 >= 0 && v1 < iconSize) { final Screen screen = ModMenu.getConfigScreen(id, list.getParent()); if (screen != null) { client.displayScreen(screen); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index 48661d885..e8caedd4e 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -106,7 +106,7 @@ public void init() { String oldText = this.searchBox == null ? "" : this.searchBox.getText(); this.searchBox = new TextFieldWidget(searchBoxX, 22, searchBoxWidth, 20, i18n.translateKey("modmenu.search")); // field_6451_g this.searchBox.setText(oldText); - this.modList = new ModListWidget(this.mc, paneWidth, this.height, paneY + 19, this.height - 36, 36, this.searchBox.getText(), this.modList, this); + this.modList = new ModListWidget(this.mc, paneWidth, this.height, paneY + 19, this.height - 36, config.getCompactList() ? 23 : 36, this.searchBox.getText(), this.modList, this); this.modList.setLeftPos(0); this.descriptionListWidget = new DescriptionListWidget(this.mc, paneWidth, this.height, paneY + 60, this.height - 36, 9 + 1, this); this.descriptionListWidget.setLeftPos(rightPaneX); @@ -358,7 +358,7 @@ public void render(int mouseX, int mouseY, float delta) { } this.selected.bindIconTexture(); - ModListEntry.internalRender(paneY, x); + ModListEntry.internalRender(paneY, x, ModListEntry.FULL_ICON_SIZE); int lineSpacing = 9 + 1; int imageOffset = 36; String translationKey = "modmenu.nameTranslation." + id; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java index 9efb978ed..790cfad28 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java @@ -170,6 +170,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { setScrollAmount(Math.max(0, this.getMaxPosition() - (this.bottom - this.top - 4))); } } + @Override protected void renderList(int x, int y, int mouseX, int mouseY, float delta) { int itemCount = this.getItemCount(); diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java b/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java index d7bd36f6d..d4333613c 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java @@ -1,6 +1,8 @@ package io.github.prospector.modmenu.gui.entries; import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; import io.github.prospector.modmenu.gui.ModListEntry; import io.github.prospector.modmenu.gui.ModListWidget; import io.github.prospector.modmenu.util.ModListSearch; @@ -21,6 +23,8 @@ public class ParentEntry extends ModListEntry { protected List children; protected ModListWidget list; protected boolean hoveringIcon = false; + protected long sinceLastClick = 0; + ModMenuConfig config = ModMenuConfigManager.getConfig(); public ParentEntry(Minecraft mc, ModContainer parent, List children, ModListWidget list) { super(mc, parent, list); @@ -34,39 +38,45 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou FontRenderer font = this.fontRenderer; int childrenBadgeHeight = 9; int childrenBadgeWidth = 9; - int children = ModListSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); - int childrenWidth = font.stringWidth(Integer.toString(children)) - 1; + int shownChildren = ModListSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); + String childrenStr = shownChildren == children.size() + ? Integer.toString(shownChildren) + : shownChildren + "/" + children.size(); + int childrenWidth = font.stringWidth(childrenStr) - 1; if (childrenBadgeWidth < childrenWidth + 4) { childrenBadgeWidth = childrenWidth + 4; } - int childrenBadgeX = x + 32 - childrenBadgeWidth; - int childrenBadgeY = y + 32 - childrenBadgeHeight; - int childrenOutlineColor = 0x8810d098; - int childrenFillColor = 0x88046146; + int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + int childrenBadgeX = x + iconSize - childrenBadgeWidth; + int childrenBadgeY = y + iconSize - childrenBadgeHeight; + int childrenOutlineColor = 0xff107454; + int childrenFillColor = 0xff093929; drawRect(childrenBadgeX + 1, childrenBadgeY, childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + 1, childrenOutlineColor); drawRect(childrenBadgeX, childrenBadgeY + 1, childrenBadgeX + 1, childrenBadgeY + childrenBadgeHeight - 1, childrenOutlineColor); drawRect(childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + 1, childrenBadgeX + childrenBadgeWidth, childrenBadgeY + childrenBadgeHeight - 1, childrenOutlineColor); drawRect(childrenBadgeX + 1, childrenBadgeY + 1, childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + childrenBadgeHeight - 1, childrenFillColor); drawRect(childrenBadgeX + 1, childrenBadgeY + childrenBadgeHeight - 1, childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + childrenBadgeHeight, childrenOutlineColor); - this.drawStringNoShadow(font, Integer.toString(children), childrenBadgeX + childrenBadgeWidth / 2 - childrenWidth / 2, childrenBadgeY + 1, 0xCACACA); - this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + 32 && mouseY >= y - 1 && mouseY <= y - 1 + 32; + this.drawStringNoShadow(font, childrenStr, childrenBadgeX + childrenBadgeWidth / 2 - childrenWidth / 2, childrenBadgeY + 1, 0xCACACA); + + this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; if (isMouseOver(mouseX, mouseY)) { - drawRect(x, y, x + 32, y + 32, 0xA0909090); + drawRect(x, y, x + iconSize, y + iconSize, 0xA0909090); GLRenderer.pushFrame(); GLRenderer.setShader(Shaders.INTERFACE); GLRenderer.setColor4f(1, 1, 1, 1); - int xOffset = list.getParent().showModChildren.contains(getMetadata().getId()) ? 32 : 0; - int yOffset = hoveringIcon ? 32 : 0; + int xOffset = list.getParent().showModChildren.contains(getMetadata().getId()) ? iconSize : 0; + int yOffset = hoveringIcon ? iconSize : 0; + float maxUV = config.getCompactList() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; this.client.textureManager.loadTexture(PARENT_MOD_TEXTURE).bind(); TessellatorGeneral t = GLRenderer.getTessellator(); t.startDrawingQuads(); - t.addVertexWithUV(x, y, 0, xOffset / 256f, yOffset / 256f); - t.addVertexWithUV(x, y + 32, 0, xOffset / 256f, (yOffset + 32) / 256f); - t.addVertexWithUV(x + 32, y + 32, 0, (xOffset + 32) / 256f, (yOffset + 32) / 256f); - t.addVertexWithUV(x + 32, y, 0, (xOffset + 32) / 256f, yOffset / 256f); + t.addVertexWithUV(x, y, 0, xOffset / maxUV, yOffset / maxUV); + t.addVertexWithUV(x, y + iconSize, 0, xOffset / maxUV, (yOffset + iconSize) / maxUV); + t.addVertexWithUV(x + iconSize, y + iconSize, 0, (xOffset + iconSize) / maxUV, (yOffset + iconSize) / maxUV); + t.addVertexWithUV(x + iconSize, y, 0, (xOffset + iconSize) / maxUV, yOffset / maxUV); t.draw(); GLRenderer.popFrame(); @@ -75,29 +85,49 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou @Override public void mouseClicked(int mouseX, int mouseY, int i) { - if (hoveringIcon) { - String id = getMetadata().getId(); - if (list.getParent().showModChildren.contains(id)) { - list.getParent().showModChildren.remove(id); - } else { - list.getParent().showModChildren.add(id); - } - list.filter(list.getParent().getSearchInput(), false); + int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + if (mouseX >= 0 && mouseX <= iconSize) { + toggleChildren(); + return; + } else if (!config.getQuickConfigure() && System.currentTimeMillis() - sinceLastClick < 250) { + toggleChildren(); + return; } + this.sinceLastClick = System.currentTimeMillis(); super.mouseClicked(mouseX, mouseY, i); } + private void toggleChildren() { + String id = getMetadata().getId(); + if (list.getParent().showModChildren.contains(id)) { + list.getParent().showModChildren.remove(id); + } else { + list.getParent().showModChildren.add(id); + } + list.filter(list.getParent().getSearchInput(), false); + } + @Override public boolean keyPressed(int int_1, int int_2, int int_3) { + String id = getMetadata().getId(); if (int_1 == Keyboard.KEY_RETURN) { - String id = getMetadata().getId(); + toggleChildren(); + return true; + } else if (int_1 == Keyboard.KEY_LEFT) { if (list.getParent().showModChildren.contains(id)) { list.getParent().showModChildren.remove(id); - } else { - list.getParent().showModChildren.add(id); + list.filter(list.getParent().getSearchInput(), false); } - list.filter(list.getParent().getSearchInput(), false); return true; + } else if (int_1 == Keyboard.KEY_RIGHT) { + if (!list.getParent().showModChildren.contains(id)) { + list.getParent().showModChildren.add(id); + list.filter(list.getParent().getSearchInput(), false); + return true; + } else { + list.keyPressed(264, 0, 0); + return true; + } } return super.keyPressed(int_1, int_2, int_3); } diff --git a/src/main/resources/assets/modmenu/lang/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang index 3fb82b24e..0d5ed8472 100644 --- a/src/main/resources/assets/modmenu/lang/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US.lang @@ -3,6 +3,13 @@ modmenu.nameTranslation.modmenu=Mod Menu modmenu.descriptionTranslation.modmenu=Adds a mod menu to view the list of mods you have installed. modmenu.loaded=(%s Loaded) +modmenu.loaded.short=(%s) +modmenu.loaded.69.secret=(%s Loaded...nice) +modmenu.loaded.420.secret=(%s Loaded...blaze it) +modmenu.mods.n= (%s Mods) +modmenu.mods.1= (%s Mod) +modmenu.mods.69.secret= (%s Mods...nice) +modmenu.mods.420.secret= (%s Mods...blaze it) modmenu.search=Search for mods modmenu.searchTerms.library=api library @@ -97,6 +104,9 @@ option.modmenu.hide_config_buttons.false=Shown option.modmenu.hide_badges=Mod Badges option.modmenu.hide_badges.true=Hidden option.modmenu.hide_badges.false=Shown +option.modmenu.compact_list=List +option.modmenu.compact_list.true=Compact +option.modmenu.compact_list.false=Standard option.modmenu.hide_mod_links=Mod Links option.modmenu.hide_mod_links.true=Hidden option.modmenu.hide_mod_links.false=Shown @@ -106,6 +116,20 @@ option.modmenu.hide_mod_credits.false=Shown option.modmenu.hide_mod_license=Mod License option.modmenu.hide_mod_license.true=Hidden option.modmenu.hide_mod_license.false=Shown +option.modmenu.count_children=Children +option.modmenu.count_children.true=Counted +option.modmenu.count_children.false=Not Counted +option.modmenu.count_libraries=Libraries +option.modmenu.count_libraries.true=Counted +option.modmenu.count_libraries.false=Not Counted +option.modmenu.count_hidden_mods=Hidden Mods +option.modmenu.count_hidden_mods.true=Counted +option.modmenu.count_hidden_mods.false=Not Counted +option.modmenu.mod_count_location=Mod Count +option.modmenu.mod_count_location.title_screen=Title Corner +option.modmenu.mod_count_location.mods_button=Mods Button +option.modmenu.mod_count_location.title_screen_and_mods_button=Both +option.modmenu.mod_count_location.none=Neither option.modmenu.easter_eggs=Easter Eggs option.modmenu.easter_eggs.true=Enabled option.modmenu.easter_eggs.false=Disabled @@ -135,5 +159,5 @@ option.modmenu.quick_configure.true=Enabled option.modmenu.quick_configure.false=Disabled option.modmenu.update_channel=Update Channel option.modmenu.update_channel.alpha=All -option.modmenu.update_channel.beta=Release & Beta +option.modmenu.update_channel.beta=Release/Beta option.modmenu.update_channel.release=Release diff --git a/src/main/resources/assets/modmenu/lang/uk_UA.lang b/src/main/resources/assets/modmenu/lang/uk_UA.lang index ee1192c35..e359b2d0a 100644 --- a/src/main/resources/assets/modmenu/lang/uk_UA.lang +++ b/src/main/resources/assets/modmenu/lang/uk_UA.lang @@ -2,7 +2,14 @@ modmenu.title=Моди modmenu.nameTranslation.modmenu=Mod Menu modmenu.descriptionTranslation.modmenu=Додає меню для перегляду встановлених модів. -modmenu.loaded=(%s) +modmenu.loaded=(Завантажено %s) +modmenu.loaded.short=(%s) +modmenu.loaded.69.secret=(Завантажено %s… Славно) +modmenu.loaded.420.secret=(Завантажено %s… Спали їх) +modmenu.mods.n= (%s модів) +modmenu.mods.1= (%s мод) +modmenu.mods.69.secret= (%s модів… Славно) +modmenu.mods.420.secret= (%s модів… Спали їх) modmenu.search=Пошук modmenu.searchTerms.library=api бібліотека library @@ -97,6 +104,9 @@ option.modmenu.hide_config_buttons.false=Показано option.modmenu.hide_badges=Значки модів option.modmenu.hide_badges.true=Приховано option.modmenu.hide_badges.false=Показано +option.modmenu.compact_list=Перелік +option.modmenu.compact_list.true=Стислий +option.modmenu.compact_list.false=Звичайний option.modmenu.hide_mod_links=Посилання option.modmenu.hide_mod_links.true=Приховано option.modmenu.hide_mod_links.false=Показано @@ -106,6 +116,20 @@ option.modmenu.hide_mod_credits.false=Показано option.modmenu.hide_mod_license=Ліцензія option.modmenu.hide_mod_license.true=Приховано option.modmenu.hide_mod_license.false=Показано +option.modmenu.count_children=Дочірні моди +option.modmenu.count_children.true=Ураховано +option.modmenu.count_children.false=Не враховано +option.modmenu.count_libraries=Бібліотеки +option.modmenu.count_libraries.true=Ураховано +option.modmenu.count_libraries.false=Не враховано +option.modmenu.count_hidden_mods=Сховані моди +option.modmenu.count_hidden_mods.true=Ураховано +option.modmenu.count_hidden_mods.false=Не враховано +option.modmenu.mod_count_location=Кількість модів +option.modmenu.mod_count_location.title_screen=У кутку +option.modmenu.mod_count_location.mods_button=Кнопка +option.modmenu.mod_count_location.title_screen_and_mods_button=Усюди +option.modmenu.mod_count_location.none=Приховано option.modmenu.easter_eggs=Прихованки option.modmenu.easter_eggs.true=УВІМК. option.modmenu.easter_eggs.false=ВИМК. @@ -133,7 +157,7 @@ option.modmenu.button_update_badge.false=Приховано option.modmenu.quick_configure=Швидке налаштування option.modmenu.quick_configure.true=УВІМК. option.modmenu.quick_configure.false=ВИМК. -option.modmenu.update_channel=Типи оновлень +option.modmenu.update_channel=Тип оновлень option.modmenu.update_channel.alpha=Усі option.modmenu.update_channel.beta=Випуск/бета option.modmenu.update_channel.release=Випуск From 7eb94b9a78e956b807e4df0c21da614802291bbf Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Thu, 15 Oct 2026 09:06:48 +0300 Subject: [PATCH 53/59] summary --- .../prospector/modmenu/gui/ModListEntry.java | 11 ++++++-- .../prospector/modmenu/gui/ModListScreen.java | 6 ++--- .../modmenu/util/ModListSearch.java | 25 +++++++++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 7a9cae272..ae688fcfd 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -154,10 +154,17 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou String prefixedVersion = VersionUtil.getPrefixedVersion(versionString); RenderUtils.INSTANCE.drawWrappedString(this, prefixedVersion, x + iconSize + 3, y + lineHeight + 2, rowWidth - iconSize - 7, 2, 0x808080); } else { + String description; + String descKey = "modmenu.descriptionTranslation." + id; String translatedDesc = i18n.translateKey(descKey); - String description; - if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { + + String sumKey = "modmenu.summaryTranslation." + id; + String translatedSum = i18n.translateKey(sumKey); + + if (!translatedSum.equals(sumKey) && config.getTranslateDescriptions()) { + description = translatedSum; + } else if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { description = translatedDesc; } else { description = metadata.getDescription(); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java index e8caedd4e..e920fe3b2 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java @@ -202,12 +202,12 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { protected void buttonClicked(ButtonElement button) { switch (button.id) { case CONFIGURE_BUTTON_ID: { - final String modid = Objects.requireNonNull(selected).getMetadata().getId(); - final Screen screen = ModMenu.getConfigScreen(modid, this); + final String id = Objects.requireNonNull(selected).getMetadata().getId(); + final Screen screen = ModMenu.getConfigScreen(id, this); if (screen != null) { mc.displayScreen(screen); } else { - ModMenu.openConfigScreen(modid); + ModMenu.openConfigScreen(id); } break; } diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index 21fb4d2df..f1dd82845 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -31,29 +31,32 @@ public static List search(ModListScreen screen, String query, List private static boolean passesFilters(ModListScreen screen, ModContainer container, String query) { ModMetadata metadata = container.getMetadata(); - String modId = metadata.getId(); - - //Some basic search, could do with something more advanced but this will do for now + String id = metadata.getId(); I18n i18n = I18n.getInstance(); ModMenuConfig config = ModMenuConfigManager.getConfig(); - String nameTranslation = "modmenu.nameTranslation." + modId; + String nameTranslation = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(nameTranslation); String modName = !translatedName.equals(nameTranslation) && config.getTranslateNames() ? translatedName : metadata.getName(); - String descriptionTranslation = "modmenu.descriptionTranslation." + modId; + String descriptionTranslation = "modmenu.descriptionTranslation." + id; String translatedDescription = i18n.translateKey(descriptionTranslation); String modDescription = !translatedDescription.equals(descriptionTranslation) && config.getTranslateDescriptions() ? translatedDescription : metadata.getDescription(); + String summaryTranslation = "modmenu.summaryTranslation." + id; + String translatedSummary = i18n.translateKey(summaryTranslation); + String modSummary = !translatedSummary.equals(summaryTranslation) && config.getTranslateDescriptions() ? translatedSummary : metadata.getDescription(); + if (modName.toLowerCase(Locale.ROOT).contains(query) // Search mod name || modDescription.toLowerCase(Locale.ROOT).contains(query) // Search mod description - || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod id + || modSummary.toLowerCase(Locale.ROOT).contains(query) // Search mod summary + || id.toLowerCase(Locale.ROOT).contains(query) // Search mod id || authorMatches(container, query) // Search via author - || (ModMenu.LIBRARY_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.library").toLowerCase(Locale.ROOT).contains(query)) // Search for lib mods - || (ModMenu.CLIENTSIDE_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.clientside").toLowerCase(Locale.ROOT).contains(query)) // Search for clientside mods - || (ModMenu.DEPRECATED_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.deprecated").toLowerCase(Locale.ROOT).contains(query)) // Search for deprecated mods - || (ModMenu.PATCHWORK_FORGE_MODS.contains(modId) && i18n.translateKey("modmenu.searchTerms.patchwork").toLowerCase(Locale.ROOT).contains(query)) // Search for forge and patchwork - || (ModMenu.hasConfigScreenFactory(modId) && i18n.translateKey("modmenu.searchTerms.configurable").toLowerCase(Locale.ROOT).contains(query)) // Search for mods that can be configured + || (ModMenu.LIBRARY_MODS.contains(id) && i18n.translateKey("modmenu.searchTerms.library").toLowerCase(Locale.ROOT).contains(query)) // Search for lib mods + || (ModMenu.CLIENTSIDE_MODS.contains(id) && i18n.translateKey("modmenu.searchTerms.clientside").toLowerCase(Locale.ROOT).contains(query)) // Search for clientside mods + || (ModMenu.DEPRECATED_MODS.contains(id) && i18n.translateKey("modmenu.searchTerms.deprecated").toLowerCase(Locale.ROOT).contains(query)) // Search for deprecated mods + || (ModMenu.PATCHWORK_FORGE_MODS.contains(id) && i18n.translateKey("modmenu.searchTerms.patchwork").toLowerCase(Locale.ROOT).contains(query)) // Search for forge and patchwork + || (ModMenu.hasConfigScreenFactory(id) && i18n.translateKey("modmenu.searchTerms.configurable").toLowerCase(Locale.ROOT).contains(query)) // Search for mods that can be configured ) { return true; } From 595f46d74c3012cedab6b57a2112c82318feb8ec Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:52:49 +0300 Subject: [PATCH 54/59] fix parents and children --- .../prospector/modmenu/gui/ModListEntry.java | 8 +- .../prospector/modmenu/gui/ModListWidget.java | 186 +++++++++++++----- .../modmenu/gui/entries/ChildEntry.java | 17 +- .../modmenu/gui/entries/ParentEntry.java | 12 +- 4 files changed, 167 insertions(+), 56 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index ae688fcfd..1c1c198cd 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -243,12 +243,12 @@ private BufferedImage createIcon() { public void mouseClicked(int v, int v1, int i) { list.select(this); - if (i == 0 && !(this instanceof ParentEntry)) { + if (i == 0 && !(this instanceof ParentEntry) && config.getQuickConfigure()) { String id = metadata.getId(); - if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id) && config.getQuickConfigure()) { + if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id)) { int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - int iconLeft = getXOffset(); - if (v >= iconLeft && v < iconLeft + iconSize && v1 >= 0 && v1 < iconSize) { + int iconLeft = list.getRowLeft() + getXOffset(); + if (v >= iconLeft && v < iconLeft + iconSize) { final Screen screen = ModMenu.getConfigScreen(id, list.getParent()); if (screen != null) { client.displayScreen(screen); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java index 790cfad28..06e9a95cf 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java @@ -31,6 +31,10 @@ public class ModListWidget extends AlwaysSelectedEntryListWidget i private Set addedMods = new HashSet<>(); private String selectedModId = null; + private boolean scrollBarDragging = false; + private double scrollBarGrabbedY = 0; + private double scrollBarGrabOffset = 0; + ModMenuConfig config = ModMenuConfigManager.getConfig(); public ModListWidget(Minecraft client, int width, int height, int y1, int y2, int entryHeight, String searchTerm, ModListWidget list, ModListScreen parent) { @@ -100,11 +104,44 @@ public void reloadFilters() { filter(parent.getSearchInput(), true, false); } - public void filter(String searchTerm, boolean refresh) { filter(searchTerm, refresh, true); } + private List getChildrenFor(String parentId) { + for (Map.Entry> e : ModMenu.PARENT_MAP.entrySet()) { + if (e.getKey().getMetadata().getId().equals(parentId)) { + return e.getValue(); + } + } + return null; + } + + private boolean isParent(String modId) { + for (ModContainer c : ModMenu.PARENT_MAP.keySet()) { + if (c.getMetadata().getId().equals(modId)) return true; + } + return false; + } + + private boolean isChild(String modId) { + for (List list : ModMenu.PARENT_MAP.values()) { + for (ModContainer c : list) { + if (c.getMetadata().getId().equals(modId)) return true; + } + } + return false; + } + + private boolean hasVisibleChildMods(ModContainer parent) { + List children = getChildrenFor(parent.getMetadata().getId()); + if (children == null) return false; + return !children.stream().allMatch(child -> { + String childId = child.getMetadata().getId(); + return ModMenu.LIBRARY_MODS.contains(childId) && !config.getShowLibraries(); + }); + } + private void filter(String searchTerm, boolean refresh, boolean search) { this.clearEntries(); addedMods.clear(); @@ -125,25 +162,29 @@ private void filter(String searchTerm, boolean refresh, boolean search) { for (ModContainer container : matched) { ModMetadata metadata = container.getMetadata(); - String modId = metadata.getId(); - boolean library = ModMenu.LIBRARY_MODS.contains(modId); + String id = metadata.getId(); + boolean library = ModMenu.LIBRARY_MODS.contains(id); //Hide parent lib mods when the config is set to hide if (library && !config.getShowLibraries()) { continue; } - if (!ModMenu.PARENT_MAP.values().contains(container)) { - if (ModMenu.PARENT_MAP.keySet().contains(container)) { + if (!isChild(id)) { + if (isParent(id) && hasVisibleChildMods(container)) { //Add parent mods when not searching - List children = ModMenu.PARENT_MAP.get(container); + List children = getChildrenFor(id); children.sort(config.getSorting().getComparator()); ParentEntry parent = new ParentEntry(minecraft, container, children, this); this.addEntry(parent); //Add children if they are meant to be shown - if (this.parent.showModChildren.contains(modId)) { + if (this.parent.showModChildren.contains(id)) { List validChildren = ModListSearch.search(this.parent, searchTerm, children); for (ModContainer child : validChildren) { + String childId = child.getMetadata().getId(); + if (ModMenu.LIBRARY_MODS.contains(childId) && !config.getShowLibraries()) { + continue; + } this.addEntry(new ChildEntry(minecraft, child, parent, this, validChildren.indexOf(child) == validChildren.size() - 1)); } } @@ -170,7 +211,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { setScrollAmount(Math.max(0, this.getMaxPosition() - (this.bottom - this.top - 4))); } } - + @Override protected void renderList(int x, int y, int mouseX, int mouseY, float delta) { int itemCount = this.getItemCount(); @@ -217,56 +258,80 @@ protected void renderList(int x, int y, int mouseX, int mouseY, float delta) { } GLRenderer.popFrame(); - } @Override - public void mouseClicked(int double_1, int double_2, int int_1) { - this.updateScrollingState(double_1, double_2, int_1); - if (this.isMouseOver(double_1, double_2)) { - ModListEntry entry = this.getEntryAtPos(double_1, double_2); + public void mouseClicked(int mouseX, int mouseY, int mouseButton) { + this.updateScrollingState(mouseX, mouseY, mouseButton); + if (this.isMouseOver(mouseX, mouseY)) { + ModListEntry entry = this.getEntryAtPos(mouseX, mouseY); if (entry != null) { - if (int_1 == 0 && !(entry instanceof ParentEntry) && config.getQuickConfigure()) { - String id = entry.getMetadata().getId(); - if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id)) { - int iconLeft = getRowLeft() + entry.getXOffset(); - if (double_1 >= iconLeft && double_1 < iconLeft + 32) { - int index = children().indexOf(entry); - if (index >= 0) { - int entryTop = this.getRowTop(index) + 2; - if (double_2 >= entryTop && double_2 < entryTop + 32) { - final Screen screen = ModMenu.getConfigScreen(id, parent); - if (screen != null) { - minecraft.displayScreen(screen); - } else { - ModMenu.openConfigScreen(id); - } - return; - } - } - } - } - } - - if (entry.list.getFocused() != null) { - if (!entry.list.getFocused().equals(entry)) { - this.setFocused(entry); - this.setSelected(entry); - this.setDragging(true); - super.mouseClicked(double_1, double_2, int_1); - } - } else { + if (entry.list.getFocused() == null || !entry.list.getFocused().equals(entry)) { this.setFocused(entry); this.setSelected(entry); this.setDragging(true); - super.mouseClicked(double_1, double_2, int_1); } - } else if (int_1 == 0) { - this.clickedHeader((int) (double_1 - (double) (this.left + this.width / 2 - this.getRowWidth() / 2)), (int) (double_2 - (double) this.top) + (int) this.getScrollAmount() - 4); + entry.mouseClicked(mouseX, mouseY, mouseButton); + } else if (mouseButton == 0) { + this.clickedHeader( + (int) (mouseX - (double) (this.left + this.width / 2 - this.getRowWidth() / 2)), + (int) (mouseY - (double) this.top) + (int) this.getScrollAmount() - 4 + ); } } } + protected void updateScrollingState(double mouseX, double mouseY, int mouseButton) { + this.scrollBarDragging = false; + if (mouseButton == 0 + && mouseX >= (double) this.getScrollbarPosition() + && mouseX < (double) (this.getScrollbarPosition() + 6)) { + this.scrollBarDragging = true; + this.scrollBarGrabbedY = mouseY; + if (this.getMaxPosition() > 0) { + int trackHeight = this.bottom - this.top - 4; + int thumbHeight = Math.max(32, trackHeight * trackHeight / this.getMaxPosition()); + int thumbTop = (int) this.getScrollAmount() * (trackHeight - thumbHeight) / this.getMaxScroll() + this.top; + if (thumbTop < this.top) { + thumbTop = this.top; + } + if (thumbTop + thumbHeight > this.bottom) { + thumbTop = this.bottom - thumbHeight; + } + this.scrollBarGrabOffset = thumbTop - mouseY; + } else { + this.scrollBarGrabOffset = 0; + } + } + } + + protected void clickedHeader(int x, int y) { + } + + @Override + public void mouseDragged(double mouseX, double mouseY, int mouseButton, double deltaX, double deltaY) { + if (this.scrollBarDragging && mouseButton == 0) { + int trackHeight = this.bottom - this.top - 4; + if (this.getMaxPosition() > 0 && trackHeight > 0) { + int thumbHeight = Math.max(32, trackHeight * trackHeight / this.getMaxPosition()); + double thumbTop = mouseY + this.scrollBarGrabOffset - this.top; + double scrollable = trackHeight - thumbHeight; + if (scrollable > 0) { + double amount = thumbTop / scrollable * this.getMaxScroll(); + this.setScrollAmount(amount); + } + } + } else { + super.mouseDragged(mouseX, mouseY, mouseButton, deltaX, deltaY); + } + } + + @Override + public void mouseReleased(int mouseX, int mouseY, int mouseButton) { + this.scrollBarDragging = false; + super.mouseReleased(mouseX, mouseY, mouseButton); + } + @Override public void mouseScrolled(double mouseX, double mouseY, double scrollDelta) { if (this.isMouseOver(mouseX, mouseY)) { @@ -274,6 +339,33 @@ public void mouseScrolled(double mouseX, double mouseY, double scrollDelta) { } } + public void ensureVisible(ModListEntry entry) { + int index = this.children().indexOf(entry); + if (index < 0) return; + int i = this.getRowTop(index); + int j = i - this.top - 4 - this.itemHeight; + if (j < 0) { + this.setScrollAmount(this.getScrollAmount() + j); + } + + int k = this.bottom - i - (this.itemHeight * 2); + if (k < 0) { + this.setScrollAmount(this.getScrollAmount() - k); + } + } + + public void keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == 264 || keyCode == 265) { + super.keyPressed(keyCode, scanCode, modifiers); + return; + } + + ModListEntry selected = getSelected(); + if (selected != null) { + selected.keyPressed(keyCode, scanCode, modifiers); + } + } + public final ModListEntry getEntryAtPos(double x, double y) { int int_5 = MathHelper.floor(y - (double) this.top) - this.headerHeight + (int) this.getScrollAmount() - 4; // convertToBlockCoord int index = int_5 / this.itemHeight; @@ -291,7 +383,7 @@ public int getRowWidth() { } @Override - protected int getRowLeft() { + public int getRowLeft() { return left + 6; } diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java b/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java index 91017df62..96dcbf400 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java @@ -4,6 +4,7 @@ import io.github.prospector.modmenu.gui.ModListWidget; import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; +import org.lwjgl.input.Keyboard; public class ChildEntry extends ModListEntry { private boolean bottomChild; @@ -18,10 +19,20 @@ public ChildEntry(Minecraft mc, ModContainer container, ParentEntry parent, ModL @Override public void render(int index, int y, int x, int rowWidth, int rowHeight, int mouseX, int mouseY, boolean isSelected, float delta) { super.render(index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); - x += 4; + x -= 2; int color = 0xFFA0A0A0; - drawRect(x, y - 2, x + 1, y + (bottomChild ? rowHeight / 2 : rowHeight + 2), color); - drawRect(x, y + rowHeight / 2, x + 7, y + rowHeight / 2 + 1, color); + drawRect(x, y - 2, x + 1, y + (bottomChild ? rowHeight / 2 : rowHeight + 2), color); + drawRect(x, y + rowHeight / 2, x + 7, y + rowHeight / 2 + 1, color); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == Keyboard.KEY_LEFT) { + list.select(parent); + list.ensureVisible(parent); + return true; + } + return super.keyPressed(keyCode, scanCode, modifiers); } @Override diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java b/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java index d4333613c..340b88c73 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java @@ -49,6 +49,11 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; int childrenBadgeX = x + iconSize - childrenBadgeWidth; int childrenBadgeY = y + iconSize - childrenBadgeHeight; + + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.INTERFACE); + GLRenderer.setColor4f(1, 1, 1, 1); + int childrenOutlineColor = 0xff107454; int childrenFillColor = 0xff093929; drawRect(childrenBadgeX + 1, childrenBadgeY, childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + 1, childrenOutlineColor); @@ -56,7 +61,9 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou drawRect(childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + 1, childrenBadgeX + childrenBadgeWidth, childrenBadgeY + childrenBadgeHeight - 1, childrenOutlineColor); drawRect(childrenBadgeX + 1, childrenBadgeY + 1, childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + childrenBadgeHeight - 1, childrenFillColor); drawRect(childrenBadgeX + 1, childrenBadgeY + childrenBadgeHeight - 1, childrenBadgeX + childrenBadgeWidth - 1, childrenBadgeY + childrenBadgeHeight, childrenOutlineColor); - this.drawStringNoShadow(font, childrenStr, childrenBadgeX + childrenBadgeWidth / 2 - childrenWidth / 2, childrenBadgeY + 1, 0xCACACA); + this.drawStringNoShadow(font, childrenStr, childrenBadgeX + childrenBadgeWidth / 2 - childrenWidth / 2, childrenBadgeY + 1, 0xFFCACACA); + + GLRenderer.popFrame(); this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; if (isMouseOver(mouseX, mouseY)) { @@ -86,7 +93,8 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou @Override public void mouseClicked(int mouseX, int mouseY, int i) { int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - if (mouseX >= 0 && mouseX <= iconSize) { + int iconLeft = list.getRowLeft() + getXOffset(); + if (mouseX >= iconLeft && mouseX < iconLeft + iconSize) { toggleChildren(); return; } else if (!config.getQuickConfigure() && System.currentTimeMillis() - sinceLastClick < 250) { From 39815ad79891a2494eea03fdf0a4738c71a856a4 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:37:35 +0300 Subject: [PATCH 55/59] roles and bta update check --- .../modmenu/gui/DescriptionListWidget.java | 33 +++++- .../modmenu/util/UpdateCheckerUtil.java | 103 ++++++++++++++++-- .../modmenu/util/mod/BTAUpdateInfo.java | 41 +++++++ .../resources/assets/modmenu/lang/en_US.lang | 10 +- .../resources/assets/modmenu/lang/uk_UA.lang | 10 +- 5 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java index 9c8b87f8a..92483687e 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java @@ -239,16 +239,34 @@ public void render(int mouseX, int mouseY, float delta) { children().add(new DescriptionEntry(i18n.translateKey("modmenu.credits"))); if (!authors.isEmpty()) { - children().add(new DescriptionEntry(" " + i18n.translateKey("modmenu.authors"))); + int indent = 8; + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, creditsRoleText("Author"), getRowWidth() - 16)) { + children().add(new DescriptionEntry(line, indent)); + indent = 16; + } for (Person person : authors) { - children().add(new DescriptionEntry(" " + person.getName())); + indent = 16; + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, person.getName(), getRowWidth() - 24)) { + children().add(new DescriptionEntry(line, indent)); + indent = 24; + } } } if (!contributors.isEmpty()) { - children().add(new DescriptionEntry(" " + i18n.translateKey("modmenu.contributors"))); + if (!authors.isEmpty()) children().add(new DescriptionEntry("")); + + int indent = 8; + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, creditsRoleText("Contributor"), getRowWidth() - 16)) { + children().add(new DescriptionEntry(line, indent)); + indent = 16; + } for (Person person : contributors) { - children().add(new DescriptionEntry(" " + person.getName())); + indent = 16; + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, person.getName(), getRowWidth() - 24)) { + children().add(new DescriptionEntry(line, indent)); + indent = 24; + } } } } @@ -272,6 +290,13 @@ protected void renderHoleBackground(int y1, int y2, int startAlpha, int endAlpha parent.overlayBackground(left, y1, right, y2, 64, 64, 64, startAlpha, endAlpha); } + private String creditsRoleText(String roleName) { + String translationKey = "modmenu.credits.role." + roleName.replaceAll("[ -]", "_").toLowerCase(); + String fallback = roleName.endsWith("r") ? roleName + "s" : roleName; + String translated = I18n.getInstance().translateKey(translationKey); + return (translated.equals(translationKey) ? fallback : translated) + ":"; + } + protected static class DescriptionEntry extends Entry { protected String text; protected int indent; diff --git a/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java b/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java index 8f6de300f..a06a117d7 100644 --- a/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/UpdateCheckerUtil.java @@ -4,11 +4,8 @@ import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import io.github.prospector.modmenu.ModMenu; -import io.github.prospector.modmenu.api.UpdateChannel; -import io.github.prospector.modmenu.api.UpdateChecker; -import io.github.prospector.modmenu.api.UpdateInfo; -import io.github.prospector.modmenu.util.mod.Mod; -import io.github.prospector.modmenu.util.mod.ModrinthUpdateInfo; +import io.github.prospector.modmenu.api.*; +import io.github.prospector.modmenu.util.mod.*; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,16 +17,15 @@ import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; +import java.util.concurrent.*; public class UpdateCheckerUtil { public static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu/Update Checker"); private static boolean modrinthApiV2Removed = false; + private static final String BTA_VERSIONS_URL = "https://downloads.betterthanadventure.net/bta-client/release/versions.json"; + private static boolean allowsUpdateChecks(Mod mod) { return mod.allowsUpdateChecks(); } @@ -40,6 +36,8 @@ public static void checkForUpdates() { } private static void checkForUpdates0() { + checkBtaUpdate(); + List withoutUpdateChecker = new ArrayList<>(); Map currentVersions = null; Map updatedVersions = null; @@ -127,6 +125,93 @@ private static void checkForUpdates0() { } } + private static void checkBtaUpdate() { + Mod btaMod = ModMenu.MODS.get("minecraft"); + if (btaMod == null) { + LOGGER.debug("BTA mod not found in MODS map, skipping BTA update check"); + return; + } + + if (!allowsUpdateChecks(btaMod)) { + LOGGER.debug("Update checks disabled for BTA, skipping"); + return; + } + + String currentVersion = btaMod.getVersion(); + LOGGER.debug("Current BTA version: {}", currentVersion); + + try { + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() + .GET() + .uri(URI.create(BTA_VERSIONS_URL)); + + HttpResponse response = HttpUtil.request(requestBuilder, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200) { + LOGGER.debug("BTA update check: HTTP {}", response.statusCode()); + return; + } + + JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); + if (!json.has("default")) { + LOGGER.debug("BTA update check: versions.json has no 'default' field"); + return; + } + + String latestVersion = json.get("default").getAsString(); + if (latestVersion.startsWith("v")) { + latestVersion = latestVersion.substring(1); + } + + LOGGER.debug("Latest BTA version from versions.json: {}", latestVersion); + + if (isNewer(latestVersion, currentVersion)) { + btaMod.setUpdateInfo(new BTAUpdateInfo( + latestVersion, + latestVersion, + latestVersion, + UpdateChannel.RELEASE + )); + LOGGER.info("Update available for BTA: {} -> {}", currentVersion, latestVersion); + } else { + LOGGER.debug("BTA is up to date ({} >= {})", currentVersion, latestVersion); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Error checking BTA update: ", e); + } catch (IOException | RuntimeException e) { + LOGGER.error("Error checking BTA update: ", e); + } + } + + private static boolean isNewer(String latest, String current) { + int[] a = parseVersion(latest); + int[] b = parseVersion(current); + int len = Math.max(a.length, b.length); + for (int i = 0; i < len; i++) { + int ai = i < a.length ? a[i] : 0; + int bi = i < b.length ? b[i] : 0; + if (ai != bi) { + return ai > bi; + } + } + return false; + } + + private static int[] parseVersion(String version) { + String cleaned = version.replaceAll("[^0-9._]", ""); + String[] parts = cleaned.split("[._]"); + int[] result = new int[parts.length]; + for (int i = 0; i < parts.length; i++) { + try { + result[i] = Integer.parseInt(parts[i]); + } catch (NumberFormatException e) { + result[i] = 0; + } + } + return result; + } + private static Map> getModHashes(Collection mods) { Map> results = new HashMap<>(); diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java b/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java new file mode 100644 index 000000000..a9102f906 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java @@ -0,0 +1,41 @@ +package io.github.prospector.modmenu.util.mod; + +import io.github.prospector.modmenu.api.UpdateChannel; +import io.github.prospector.modmenu.api.UpdateInfo; +import io.github.prospector.modmenu.util.VersionUtil; +import net.minecraft.core.lang.I18n; +import org.jetbrains.annotations.NotNull; + +public record BTAUpdateInfo(String projectId, String versionId, String versionNumber, + UpdateChannel updateChannel) implements UpdateInfo { + + @Override + public boolean isUpdateAvailable() { + return true; + } + + @Override + public @NotNull UpdateChannel getUpdateChannel() { + return updateChannel; + } + + @Override + public @NotNull String getUpdateMessage() { + String versionNoPrefix = VersionUtil.stripPrefix(this.versionNumber); + String key = "modmenu.updateText"; + if (versionNoPrefix.isEmpty() || !Character.isDigit(versionNoPrefix.charAt(0))) { + key += ".nonNumerical"; + } + return I18n.getInstance().translateKeyAndFormat( + key, + versionNoPrefix, + I18n.getInstance().translateKey("modmenu.bta") + ); + } + + @Override + public String getDownloadLink() { + return "https://downloads.betterthanadventure.net/bta-client/release/v%s/bta.v%s.mmc.zip" + .formatted(projectId, versionId); + } +} \ No newline at end of file diff --git a/src/main/resources/assets/modmenu/lang/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang index 0d5ed8472..aefbbcb9c 100644 --- a/src/main/resources/assets/modmenu/lang/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US.lang @@ -55,6 +55,7 @@ modmenu.install_version=Install version %s modmenu.downloadLink=Download modmenu.bluesky=Bluesky +modmenu.bta=Downloads BTA! modmenu.buymeacoffee=Buy Me a Coffee modmenu.coindrop=Coindrop modmenu.crowdin=Crowdin @@ -77,8 +78,13 @@ modmenu.wiki=Wiki modmenu.twitter=X (Twitter) modmenu.youtube=YouTube -modmenu.authors=Authors: -modmenu.contributors=Contributors: +modmenu.credits.role.author=Authors +modmenu.credits.role.contributor=Contributors +modmenu.credits.role.translator=Translators +modmenu.credits.role.maintainer=Maintainers +modmenu.credits.role.playtester=Playtesters +modmenu.credits.role.illustrator=Illustrators +modmenu.credits.role.owner=Owners modmenu.modsFolder=Open Mods Folder modmenu.configsFolder=Open Configs Folder diff --git a/src/main/resources/assets/modmenu/lang/uk_UA.lang b/src/main/resources/assets/modmenu/lang/uk_UA.lang index e359b2d0a..e5803b9dc 100644 --- a/src/main/resources/assets/modmenu/lang/uk_UA.lang +++ b/src/main/resources/assets/modmenu/lang/uk_UA.lang @@ -55,6 +55,7 @@ modmenu.install_version=Установити версію %s modmenu.downloadLink=Завантажити modmenu.bluesky=Bluesky +modmenu.bta=Завантаження BTA! modmenu.buymeacoffee=Buy Me a Coffee modmenu.coindrop=Coindrop modmenu.crowdin=Crowdin @@ -77,8 +78,13 @@ modmenu.wiki=Вікі modmenu.twitter=Х (Twitter) modmenu.youtube=YouTube -modmenu.authors=Автори: -modmenu.contributors=Дописувачі: +modmenu.credits.role.author=Автори +modmenu.credits.role.contributor=Дописувачі +modmenu.credits.role.translator=Перекладачі +modmenu.credits.role.maintainer=Супроводжувачі +modmenu.credits.role.playtester=Тестери +modmenu.credits.role.illustrator=Ілюстратори +modmenu.credits.role.owner=Власники modmenu.modsFolder=Відкрити теку модів modmenu.configsFolder=Відкрити теку налаштувань From a7cb351e13685914e7369cee5c361c5b96be0600 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:39:48 +0300 Subject: [PATCH 56/59] russian and fix \n in entry --- .../prospector/modmenu/gui/ModListEntry.java | 19 +- .../resources/assets/modmenu/lang/ru_RU.lang | 184 ++++++++++++++++-- 2 files changed, 183 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java index 1c1c198cd..206b89f81 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java @@ -177,7 +177,24 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou } if (description != null) { - RenderUtils.INSTANCE.drawWrappedString(this, description, x + iconSize + 3 + 4, y + lineHeight + 2, rowWidth - iconSize - 7, 2, 0x808080); + int textX = x + iconSize + 3 + 4; + int textY = y + lineHeight + 2; + int maxWidth = rowWidth - iconSize - 7; + int maxLines = 2; + int currentLine = 0; + + String[] paragraphs = description.split("\r?\n"); + + for (String paragraph : paragraphs) { + if (currentLine >= maxLines) break; + + for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(font, paragraph, maxWidth)) { + if (currentLine >= maxLines) break; + + this.drawStringNoShadow(font, line, textX, textY + (currentLine * lineHeight), 0x808080); + currentLine++; + } + } } } diff --git a/src/main/resources/assets/modmenu/lang/ru_RU.lang b/src/main/resources/assets/modmenu/lang/ru_RU.lang index fbb6a3936..1e334b841 100644 --- a/src/main/resources/assets/modmenu/lang/ru_RU.lang +++ b/src/main/resources/assets/modmenu/lang/ru_RU.lang @@ -1,23 +1,169 @@ modmenu.title=Моды +modmenu.nameTranslation.modmenu=Mod Menu +modmenu.descriptionTranslation.modmenu=Удобное меню для просмотра списка установленных модов. + modmenu.loaded=(загружено: %s) -modmenu.config=Изменить настройки -modmenu.modsFolder=Открыть папку модов -modmenu.configsFolder=Открыть папку настроек -modmenu.configure=Настроить... +modmenu.loaded.short=(%s) +modmenu.loaded.69.secret=(загружено: %s... идеально) +modmenu.loaded.420.secret=(загружено: %s... огоньку?) +modmenu.mods.n= (%s модов) +modmenu.mods.1= (%s мод) +modmenu.mods.69.secret=(%s модов... идеально) +modmenu.mods.420.secret= (%s модов... огоньку?) + +modmenu.search=Поиск модов +modmenu.searchTerms.library=апи библиотека +modmenu.searchTerms.clientside=клиент клиентский клиентские +modmenu.searchTerms.deprecated=устаревший доисторический старый +modmenu.searchTerms.patchwork=patchwork forge fml фордж +modmenu.searchTerms.configurable=настройки настраиваемый настраиваемые опции + +modmenu.toggleFilterOptions=Фильтры +modmenu.showingMods.n=Показано %s модов +modmenu.showingMods.1=Показан %s мод +modmenu.showingLibraries.n=Показано %s библиотек +modmenu.showingLibraries.1=Показана %s библиотека +modmenu.showingModsLibraries.n.n=Показано %s модов и %s библиотек +modmenu.showingModsLibraries.n.1=Показано %s модов и %s библиотека +modmenu.showingModsLibraries.1.n=Показан %s мод и %s библиотек +modmenu.showingModsLibraries.1.1=Показан %s мод и %s библиотека + +modmenu.badge.clientsideOnly=Клиент +modmenu.badge.library=Библиотека +modmenu.badge.deprecated=Устарел +modmenu.badge.minecraft=Minecraft +modmenu.badge.forge=Forge + modmenu.modIdToolTip=ID мода: %s +modmenu.authorPrefix=Авторы: %s +modmenu.config=Изменить настройки +modmenu.configure=Настройки... modmenu.website=Сайт -modmenu.issues=Проблемы/Вопросы -modmenu.authorPrefix=От %s -modmenu.search=Поиск модов -modmenu.clientsideOnly=Клиент -modmenu.library=Библиотека -modmenu.toggleFilterOptions=Переключить параметры фильтра -modmenu.showLibraries=Библиотеки: %s -modmenu.showLibraries.true=Показано -modmenu.showLibraries.false=Скрыто -modmenu.showingMods=Показано модов: %s -modmenu.sorting=Сортировка: %s -modmenu.sorting.ascending=A-Z -modmenu.sorting.decending=Z-A -modmenu.minecraft=Minecraft -modmenu.forge=Forge +modmenu.issues=Проблемы + +modmenu.credits=Создатели: +modmenu.viewCredits=Показать авторов +modmenu.license=Лицензия: +modmenu.links=Ссылки: +modmenu.sources=Исходный код +modmenu.hasUpdate=Доступно обновление: +modmenu.experimental=(проверка обновлений ModMenu экспериментальна) +modmenu.childHasUpdate=Доступно обновление для дочернего мода. +modmenu.updateText=v%s на %s +modmenu.updateText.nonNumerical=%s на %s +modmenu.install_version=Установить версию %s +modmenu.downloadLink=Загрузить + +modmenu.bluesky=Bluesky +modmenu.bta=Загрузки BTA! +modmenu.buymeacoffee=Buy Me a Coffee +modmenu.coindrop=Coindrop +modmenu.crowdin=Crowdin +modmenu.curseforge=CurseForge +modmenu.discord=Discord +modmenu.donate=Поддержать +modmenu.flattr=Flattr +modmenu.github_releases=Релизы на GitHub +modmenu.github_sponsors=GitHub Sponsors +modmenu.kofi=Ko-fi +modmenu.liberapay=Liberapay +modmenu.mastodon=Mastodon +modmenu.modrinth=Modrinth +modmenu.opencollective=Open Collective +modmenu.patreon=Patreon +modmenu.paypal=PayPal +modmenu.reddit=Reddit +modmenu.twitch=Twitch +modmenu.wiki=Вики +modmenu.twitter=Твиттер +modmenu.youtube=YouTube + +modmenu.credits.role.author=Авторы +modmenu.credits.role.contributor=Участники +modmenu.credits.role.translator=Переводчики +modmenu.credits.role.maintainer=Основные разработчики +modmenu.credits.role.playtester=Тестировщики +modmenu.credits.role.illustrator=Иллюстраторы +modmenu.credits.role.owner=Владельцы + +modmenu.modsFolder=Папка с модами +modmenu.configsFolder=Папка с настройками + +modmenu.nameTranslation.minecraft=Minecraft +modmenu.descriptionTranslation.minecraft=Основная игра. +modmenu.nameTranslation.fabricloader=Загрузчик Fabric +modmenu.descriptionTranslation.fabricloader=Основной загрузчик модов. +modmenu.nameTranslation.java=Java +modmenu.descriptionTranslation.java=Исполняющая система Java. +modmenu.javaDistributionName=Запущено: %s + +modmenu.options=Настройки списка модов +option.modmenu.sorting=Сортировка +option.modmenu.sorting.ascending=A-Z +option.modmenu.sorting.descending=Z-A +option.modmenu.show_libraries=Библиотеки +option.modmenu.show_libraries.true=Показаны +option.modmenu.show_libraries.false=Скрыты +option.modmenu.hide_config_buttons=Кнопка настроек +option.modmenu.hide_config_buttons.true=Скрыта +option.modmenu.hide_config_buttons.false=Показана +option.modmenu.hide_badges=Метки модов +option.modmenu.hide_badges.true=Скрыты +option.modmenu.hide_badges.false=Показаны +option.modmenu.compact_list=Список +option.modmenu.compact_list.true=Компактный +option.modmenu.compact_list.false=Обычный +option.modmenu.hide_mod_links=Ссылки мода +option.modmenu.hide_mod_links.true=Скрыты +option.modmenu.hide_mod_links.false=Показаны +option.modmenu.hide_mod_credits=Авторы мода +option.modmenu.hide_mod_credits.true=Скрыты +option.modmenu.hide_mod_credits.false=Показаны +option.modmenu.hide_mod_license=Лицензия мода +option.modmenu.hide_mod_license.true=Скрыта +option.modmenu.hide_mod_license.false=Показана +option.modmenu.count_children=Дочерние +option.modmenu.count_children.true=Учитывать +option.modmenu.count_children.false=Не учитывать +option.modmenu.count_libraries=Библиотеки +option.modmenu.count_libraries.true=Учитывать +option.modmenu.count_libraries.false=Не учитывать +option.modmenu.count_hidden_mods=Скрытые +option.modmenu.count_hidden_mods.true=Учитывать +option.modmenu.count_hidden_mods.false=Не учитывать +option.modmenu.mod_count_location=Счётчик модов +option.modmenu.mod_count_location.title_screen=В углу меню +option.modmenu.mod_count_location.mods_button=На кнопке +option.modmenu.mod_count_location.title_screen_and_mods_button=Везде +option.modmenu.mod_count_location.none=Отключён +option.modmenu.easter_eggs=Пасхалки +option.modmenu.easter_eggs.true=Включены +option.modmenu.easter_eggs.false=Выключены +option.modmenu.mods_button_style=Кнопка +option.modmenu.mods_button_style.insert=Посередине +option.modmenu.mods_button_style.icon=Отдельный значок +option.modmenu.game_menu_button_style=В игре +option.modmenu.game_menu_button_style.insert=Посередине +option.modmenu.game_menu_button_style.icon=Отдельный значок +option.modmenu.random_java_colors=Цвет Java +option.modmenu.random_java_colors.true=От поставщика +option.modmenu.random_java_colors.false=Всегда красный +option.modmenu.translate_names=Локализация названий +option.modmenu.translate_names.true=Вкл +option.modmenu.translate_names.false=Выкл +option.modmenu.translate_descriptions=Локализация описаний +option.modmenu.translate_descriptions.true=Вкл +option.modmenu.translate_descriptions.false=Выкл +option.modmenu.update_checker=Проверка обновлений +option.modmenu.update_checker.true=Вкл +option.modmenu.update_checker.false=Выкл +option.modmenu.button_update_badge=Индикатор обновлений +option.modmenu.button_update_badge.true=Вкл +option.modmenu.button_update_badge.false=Выкл +option.modmenu.quick_configure=Быстрые настройки +option.modmenu.quick_configure.true=Вкл +option.modmenu.quick_configure.false=Выкл +option.modmenu.update_channel=Канал обнов. +option.modmenu.update_channel.alpha=Все +option.modmenu.update_channel.beta=Релиз и Бета +option.modmenu.update_channel.release=Релиз From 03583ee00d49891dd242a87142edb6d3cb64add9 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:50:49 +0300 Subject: [PATCH 57/59] now your chance to be a --- .../io/github/prospector/modmenu/ModMenu.java | 22 +- .../prospector/modmenu/api/ModMenuApi.java | 6 +- .../prospector/modmenu/api/UpdateChannel.java | 6 +- .../prospector/modmenu/api/UpdateChecker.java | 2 +- .../prospector/modmenu/api/UpdateInfo.java | 2 +- .../modmenu/config/FileOnlyConfig.java | 11 + .../modmenu/config/ModMenuConfig.java | 591 +++++------------- .../modmenu/config/ModMenuConfigManager.java | 197 ++++-- .../modmenu/config/ModMenuConfigScreen.java | 238 ------- .../config/option/BooleanConfigOption.java | 65 ++ .../config/option/ConfigOptionStorage.java | 60 ++ .../config/option/EnumConfigOption.java | 63 ++ .../config/option/OptionConvertible.java | 13 + .../config/option/StringSetConfigOption.java | 30 + .../modmenu/gui/ModMenuOptionsScreen.java | 231 +++++++ .../{ModListScreen.java => ModsScreen.java} | 64 +- .../AlwaysSelectedEntryListWidget.java | 3 +- .../{ => widget}/DescriptionListWidget.java | 24 +- .../gui/{ => widget}/ModListWidget.java | 46 +- .../gui/{ => widget}/ModMenuButtonWidget.java | 6 +- .../SmallModMenuButtonWidget.java} | 14 +- .../gui/{ => widget}/TextFieldWidget.java | 9 +- .../gui/widget/UpdateAvailableBadge.java | 40 ++ .../gui/{ => widget}/entries/ChildEntry.java | 5 +- .../{ => widget/entries}/EntryListWidget.java | 6 +- .../entries/IndependentEntry.java | 5 +- .../{ => widget/entries}/ModListEntry.java | 46 +- .../gui/{ => widget}/entries/ParentEntry.java | 15 +- .../modmenu/impl/ModMenuApiImpl.java | 6 +- .../modmenu/mixin/ScreenMainMenuMixin.java | 21 +- .../modmenu/mixin/ScreenPauseMixin.java | 13 +- .../modmenu/util/BadgeRenderer.java | 13 +- .../prospector/modmenu/util/DrawingUtil.java | 6 +- .../prospector/modmenu/util/HttpUtil.java | 2 +- .../modmenu/util/ModListSearch.java | 14 +- .../modmenu/util/TestModContainer.java | 5 +- .../modmenu/util/TranslationUtil.java | 2 +- .../modmenu/util/mod/BTAUpdateInfo.java | 2 +- .../modmenu/util/mod/FabricMod.java | 8 +- .../prospector/modmenu/util/mod/Mod.java | 2 +- .../modmenu/util/mod/ModrinthUpdateInfo.java | 2 +- .../resources/assets/modmenu/lang/en_US.lang | 2 + .../resources/assets/modmenu/lang/ru_RU.lang | 2 + .../resources/assets/modmenu/lang/uk_UA.lang | 2 + 44 files changed, 981 insertions(+), 941 deletions(-) create mode 100644 src/main/java/io/github/prospector/modmenu/config/FileOnlyConfig.java delete mode 100644 src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java create mode 100644 src/main/java/io/github/prospector/modmenu/config/option/BooleanConfigOption.java create mode 100644 src/main/java/io/github/prospector/modmenu/config/option/ConfigOptionStorage.java create mode 100644 src/main/java/io/github/prospector/modmenu/config/option/EnumConfigOption.java create mode 100644 src/main/java/io/github/prospector/modmenu/config/option/OptionConvertible.java create mode 100644 src/main/java/io/github/prospector/modmenu/config/option/StringSetConfigOption.java create mode 100644 src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java rename src/main/java/io/github/prospector/modmenu/gui/{ModListScreen.java => ModsScreen.java} (89%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/AlwaysSelectedEntryListWidget.java (89%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/DescriptionListWidget.java (93%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/ModListWidget.java (91%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/ModMenuButtonWidget.java (73%) rename src/main/java/io/github/prospector/modmenu/gui/{ModMenuTexturedButtonWidget.java => widget/SmallModMenuButtonWidget.java} (82%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/TextFieldWidget.java (99%) create mode 100644 src/main/java/io/github/prospector/modmenu/gui/widget/UpdateAvailableBadge.java rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/entries/ChildEntry.java (88%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget/entries}/EntryListWidget.java (99%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/entries/IndependentEntry.java (62%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget/entries}/ModListEntry.java (85%) rename src/main/java/io/github/prospector/modmenu/gui/{ => widget}/entries/ParentEntry.java (90%) diff --git a/src/main/java/io/github/prospector/modmenu/ModMenu.java b/src/main/java/io/github/prospector/modmenu/ModMenu.java index 0fb187ba5..ccff00d87 100644 --- a/src/main/java/io/github/prospector/modmenu/ModMenu.java +++ b/src/main/java/io/github/prospector/modmenu/ModMenu.java @@ -51,7 +51,7 @@ public class ModMenu implements ClientModInitializer { private static int cachedDisplayedModCount = -1; - public static void invalidateCache() { + public static void clearModCountCache() { cachedDisplayedModCount = -1; } @@ -71,10 +71,9 @@ public static void openConfigScreen(String modid) { public static String getDisplayedModCount() { if (cachedDisplayedModCount == -1) { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - boolean includeChildren = config.getCountChildren(); - boolean includeLibraries = config.getCountLibraries(); - boolean includeHidden = config.getCountHiddenMods(); + boolean includeChildren = ModMenuConfig.COUNT_CHILDREN.getValue(); + boolean includeLibraries = ModMenuConfig.COUNT_LIBRARIES.getValue(); + boolean includeHidden = ModMenuConfig.COUNT_HIDDEN_MODS.getValue(); long count = MODS.values().stream().filter(mod -> { boolean isChild = mod.getParent() != null; @@ -100,22 +99,21 @@ public static String getDisplayedModCount() { } public static String createModsButtonText(boolean title) { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - var titleStyle = config.getModsButtonStyle(); - var gameMenuStyle = config.getGameMenuButtonStyle(); + var titleStyle = ModMenuConfig.MODS_BUTTON_STYLE.getValue(); + var gameMenuStyle = ModMenuConfig.GAME_MENU_BUTTON_STYLE.getValue(); boolean isIcon = title ? - titleStyle == ModMenuConfig.ModsButtonStyle.ICON : + titleStyle == ModMenuConfig.TitleMenuButtonStyle.ICON : gameMenuStyle == ModMenuConfig.GameMenuButtonStyle.ICON; boolean isShort = title ? - titleStyle == ModMenuConfig.ModsButtonStyle.INSERT : + titleStyle == ModMenuConfig.TitleMenuButtonStyle.INSERT : gameMenuStyle == ModMenuConfig.GameMenuButtonStyle.INSERT; I18n i18n = I18n.getInstance(); String modsText = i18n.translateKey("modmenu.title"); - if (config.getModCountLocation().isOnModsButton() && !isIcon) { + if (ModMenuConfig.MOD_COUNT_LOCATION.getValue().isOnModsButton() && !isIcon) { String count = getDisplayedModCount(); if (!isShort) { modsText += " " + i18n.translateKeyAndFormat("modmenu.loaded", count); @@ -128,7 +126,7 @@ public static String createModsButtonText(boolean title) { key = hasSpecific ? specificKey : "modmenu.loaded"; String secretKey = specificKey + ".secret"; - if (config.getEasterEggs() && !i18n.translateKey(secretKey).equals(secretKey)) { + if (ModMenuConfig.EASTER_EGGS.getValue() && !i18n.translateKey(secretKey).equals(secretKey)) { key = secretKey; } modsText += " " + i18n.translateKeyAndFormat(key, count); diff --git a/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java b/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java index b8a40956c..39a1744da 100644 --- a/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java +++ b/src/main/java/io/github/prospector/modmenu/api/ModMenuApi.java @@ -1,7 +1,7 @@ package io.github.prospector.modmenu.api; import io.github.prospector.modmenu.ModMenu; -import io.github.prospector.modmenu.gui.ModListScreen; +import io.github.prospector.modmenu.gui.ModsScreen; import io.github.prospector.modmenu.util.NullScreenFactory; import io.github.prospector.modmenu.util.TriConsumer; import net.minecraft.client.gui.Screen; @@ -18,7 +18,7 @@ public interface ModMenuApi { * @return A "Mods" Screen */ static Screen createModsScreen(Screen previous) { - return new ModListScreen(previous); + return new ModsScreen(previous); } /** @@ -85,4 +85,4 @@ default Map getProvidedUpdateCheckers() { */ default void attachCustomBadges(TriConsumer consumer) { } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java b/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java index add019d25..2de2e7b8b 100644 --- a/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java +++ b/src/main/java/io/github/prospector/modmenu/api/UpdateChannel.java @@ -1,6 +1,6 @@ package io.github.prospector.modmenu.api; -import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.config.ModMenuConfig; /** * Supported update channels, in ascending order by stability. @@ -14,6 +14,6 @@ public enum UpdateChannel { * @return the user's preferred update channel. */ public static UpdateChannel getUserPreference() { - return UpdateChannel.valueOf(ModMenuConfigManager.getConfig().getUpdateChannel().name()); + return ModMenuConfig.UPDATE_CHANNEL.getValue(); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java b/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java index 549315713..9c03302f6 100644 --- a/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java +++ b/src/main/java/io/github/prospector/modmenu/api/UpdateChecker.java @@ -13,4 +13,4 @@ public interface UpdateChecker { * @return The update info */ @Nullable UpdateInfo checkForUpdates(); -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java b/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java index 9f38f63e9..4390353ad 100644 --- a/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java +++ b/src/main/java/io/github/prospector/modmenu/api/UpdateInfo.java @@ -26,4 +26,4 @@ default String getUpdateMessage() { * @return The update channel this update is available for. */ UpdateChannel getUpdateChannel(); -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/config/FileOnlyConfig.java b/src/main/java/io/github/prospector/modmenu/config/FileOnlyConfig.java new file mode 100644 index 000000000..a8566c9b5 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/FileOnlyConfig.java @@ -0,0 +1,11 @@ +package io.github.prospector.modmenu.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface FileOnlyConfig { +} diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java index 22a5deb17..8d7c998b3 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfig.java @@ -1,442 +1,149 @@ -package io.github.prospector.modmenu.config; - -import io.github.prospector.modmenu.ModMenu; -import net.fabricmc.loader.api.ModContainer; -import net.minecraft.core.lang.I18n; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -public class ModMenuConfig { - private Sorting sorting = Sorting.ASCENDING; - private boolean countLibraries = true; - private boolean compactList = true; - private boolean countChildren = true; - private ModsButtonStyle modsButtonStyle = ModsButtonStyle.INSERT; - private GameMenuButtonStyle gameMenuButtonStyle = GameMenuButtonStyle.INSERT; - private boolean countHiddenMods = true; - private ModCountLocation modCountLocation = ModCountLocation.TITLE_SCREEN; - private boolean hideModLinks = false; - private boolean showLibraries = false; - private boolean hideModLicense = false; - private boolean hideBadges = false; - private boolean hideModCredits = false; - private boolean easterEggs = true; - private boolean randomJavaColors = true; - private boolean translateNames = true; - private boolean translateDescriptions = true; - private boolean updateChecker = true; - private boolean buttonUpdateBadge = true; - private UpdateChannel updateChannel = UpdateChannel.RELEASE; - private boolean quickConfigure = true; - private boolean hideConfigButtons = false; - private List hiddenMods = new ArrayList<>(); - private List disableUpdateChecker = new ArrayList<>(); - - public void toggleSortMode() { - this.sorting = next(this.sorting); - ModMenuConfigManager.save(); - } - - public void toggleCountLibraries() { - this.countLibraries = !this.countLibraries; - ModMenu.invalidateCache(); - ModMenuConfigManager.save(); - } - - public void toggleCompactList() { - this.compactList = !this.compactList; - ModMenuConfigManager.save(); - } - - public void toggleCountChildren() { - this.countChildren = !this.countChildren; - ModMenu.invalidateCache(); - ModMenuConfigManager.save(); - } - - public void toggleModCountLocation() { - this.modCountLocation = next(this.modCountLocation); - ModMenuConfigManager.save(); - } - - public void toggleGameMenuButtonStyle() { - this.gameMenuButtonStyle = next(this.gameMenuButtonStyle); - ModMenuConfigManager.save(); - } - - public void toggleCountHiddenMods() { - this.countHiddenMods = !this.countHiddenMods; - ModMenu.invalidateCache(); - ModMenuConfigManager.save(); - } - - public void toggleModsButtonStyle() { - this.modsButtonStyle = next(this.modsButtonStyle); - ModMenuConfigManager.save(); - } - - public void toggleHideModLinks() { - this.hideModLinks = !this.hideModLinks; - ModMenuConfigManager.save(); - } - - public void toggleShowLibraries() { - this.showLibraries = !this.showLibraries; - ModMenuConfigManager.save(); - } - - public void toggleHideModLicense() { - this.hideModLicense = !this.hideModLicense; - ModMenuConfigManager.save(); - } - - public void toggleHideBadges() { - this.hideBadges = !this.hideBadges; - ModMenuConfigManager.save(); - } - - public void toggleHideModCredits() { - this.hideModCredits = !this.hideModCredits; - ModMenuConfigManager.save(); - } - - public void toggleEasterEggs() { - this.easterEggs = !this.easterEggs; - ModMenuConfigManager.save(); - } - - public void toggleRandomJavaColors() { - this.randomJavaColors = !this.randomJavaColors; - ModMenuConfigManager.save(); - } - - public void toggleTranslateNames() { - this.translateNames = !this.translateNames; - ModMenuConfigManager.save(); - } - - public void toggleTranslateDescriptions() { - this.translateDescriptions = !this.translateDescriptions; - ModMenuConfigManager.save(); - } - - public void toggleUpdateChecker() { - this.updateChecker = !this.updateChecker; - ModMenuConfigManager.save(); - } - - public void toggleButtonUpdateBadge() { - this.buttonUpdateBadge = !this.buttonUpdateBadge; - ModMenuConfigManager.save(); - } - - public void toggleUpdateChannel() { - this.updateChannel = next(this.updateChannel); - ModMenuConfigManager.save(); - } - - public void toggleQuickConfigure() { - this.quickConfigure = !this.quickConfigure; - ModMenuConfigManager.save(); - } - - public void toggleHideConfigButtons() { - this.hideConfigButtons = !this.hideConfigButtons; - ModMenuConfigManager.save(); - } - - public Sorting getSorting() { - return sorting; - } - - public boolean getCountLibraries() { - return countLibraries; - } - - public boolean getCompactList() { - return compactList; - } - - public boolean getCountChildren() { - return countChildren; - } - - public ModCountLocation getModCountLocation() { - return modCountLocation; - } - - public GameMenuButtonStyle getGameMenuButtonStyle() { - return gameMenuButtonStyle; - } - - public boolean getCountHiddenMods() { - return countHiddenMods; - } - - public ModsButtonStyle getModsButtonStyle() { - return modsButtonStyle; - } - - public boolean getHideModLinks() { - return hideModLinks; - } - - public boolean getShowLibraries() { - return showLibraries; - } - - public boolean getHideModLicense() { - return hideModLicense; - } - - public boolean getHideBadges() { - return hideBadges; - } - - public boolean getHideModCredits() { - return hideModCredits; - } - - public boolean getEasterEggs() { - return easterEggs; - } - - public boolean getRandomJavaColors() { - return randomJavaColors; - } - - public boolean getTranslateNames() { - return translateNames; - } - - public boolean getTranslateDescriptions() { - return translateDescriptions; - } - - public boolean getUpdateChecker() { - return updateChecker; - } - - public boolean getButtonUpdateBadge() { - return buttonUpdateBadge; - } - - public UpdateChannel getUpdateChannel() { - return updateChannel; - } - - public boolean getQuickConfigure() { - return quickConfigure; - } - - public boolean getHideConfigButtons() { - return hideConfigButtons; - } - - public List getHiddenMods() { - return hiddenMods; - } - - public List getDisableUpdateChecker() { - return disableUpdateChecker; - } - - public String getSortingDisplayString() { - return optionString("sorting", this.sorting.getName()); - } - - public String getCountLibrariesDisplayString() { - return optionString("count_libraries", I18n.getInstance().translateKey("option.modmenu.count_libraries." + this.countLibraries)); - } - - public String getCompactListDisplayString() { - return optionString("compact_list", I18n.getInstance().translateKey("option.modmenu.compact_list." + this.compactList)); - } - - public String getCountChildrenDisplayString() { - return optionString("count_children", I18n.getInstance().translateKey("option.modmenu.count_children." + this.countChildren)); - } - - public String getModCountLocationDisplayString() { - return optionString("mod_count_location", this.modCountLocation.getName()); - } - - public String getGameMenuButtonStyleDisplayString() { - return optionString("game_menu_button_style", this.gameMenuButtonStyle.getName()); - } - - public String getCountHiddenModsDisplayString() { - return optionString("count_hidden_mods", I18n.getInstance().translateKey("option.modmenu.count_hidden_mods." + this.countHiddenMods)); - } - - public String getModsButtonStyleDisplayString() { - return optionString("mods_button_style", this.modsButtonStyle.getName()); - } - - public String getHideModLinksDisplayString() { - return optionString("hide_mod_links", I18n.getInstance().translateKey("option.modmenu.hide_mod_links." + this.hideModLinks)); - } - - public String getShowLibrariesDisplayString() { - return optionString("show_libraries", I18n.getInstance().translateKey("option.modmenu.show_libraries." + this.showLibraries)); - } - - public String getHideModLicenseDisplayString() { - return optionString("hide_mod_license", I18n.getInstance().translateKey("option.modmenu.hide_mod_license." + this.hideModLicense)); - } - - public String getHideBadgesDisplayString() { - return optionString("hide_badges", I18n.getInstance().translateKey("option.modmenu.hide_badges." + this.hideBadges)); - } - - public String getHideModCreditsDisplayString() { - return optionString("hide_mod_credits", I18n.getInstance().translateKey("option.modmenu.hide_mod_credits." + this.hideModCredits)); - } - - public String getEasterEggsDisplayString() { - return optionString("easter_eggs", I18n.getInstance().translateKey("option.modmenu.easter_eggs." + this.easterEggs)); - } - - public String getRandomJavaColorsDisplayString() { - return optionString("random_java_colors", I18n.getInstance().translateKey("option.modmenu.random_java_colors." + this.randomJavaColors)); - } - - public String getTranslateNamesDisplayString() { - return optionString("translate_names", I18n.getInstance().translateKey("option.modmenu.translate_names." + this.translateNames)); - } - - public String getTranslateDescriptionsDisplayString() { - return optionString("translate_descriptions", I18n.getInstance().translateKey("option.modmenu.translate_descriptions." + this.translateDescriptions)); - } - - public String getUpdateCheckerDisplayString() { - return optionString("update_checker", I18n.getInstance().translateKey("option.modmenu.update_checker." + this.updateChecker)); - } - - public String getButtonUpdateBadgeDisplayString() { - return optionString("button_update_badge", I18n.getInstance().translateKey("option.modmenu.button_update_badge." + this.buttonUpdateBadge)); - } - - public String getUpdateChannelDisplayString() { - return optionString("update_channel", this.updateChannel.getName()); - } - - public String getQuickConfigureDisplayString() { - return optionString("quick_configure", I18n.getInstance().translateKey("option.modmenu.quick_configure." + this.quickConfigure)); - } - - private static > T next(T current) { - T[] values = current.getDeclaringClass().getEnumConstants(); - return values[(current.ordinal() + 1) % values.length]; - } - - private static String optionString(String optionKey, String valueName) { - return I18n.getInstance().translateKey("option.modmenu." + optionKey) + ": " + valueName; - } - - private interface OptionEnum { - String getKey(); - default String getName() { - return I18n.getInstance().translateKey(getKey()); - } - } - - public enum Sorting implements OptionEnum { - ASCENDING(Comparator.comparing(mod -> mod.getMetadata().getName(), String.CASE_INSENSITIVE_ORDER), "option.modmenu.sorting.ascending"), - DESCENDING(ASCENDING.getComparator().reversed(), "option.modmenu.sorting.descending"); - - final Comparator comparator; - final String key; - - Sorting(Comparator comparator, String key) { - this.comparator = comparator; - this.key = key; - } - - public Comparator getComparator() { - return comparator; - } - - @Override - public String getKey() { - return key; - } - } - - public enum ModCountLocation implements OptionEnum { - TITLE_SCREEN("option.modmenu.mod_count_location.title_screen"), - MODS_BUTTON("option.modmenu.mod_count_location.mods_button"), - TITLE_SCREEN_AND_MODS_BUTTON("option.modmenu.mod_count_location.title_screen_and_mods_button"), - NONE("option.modmenu.mod_count_location.none"); - - final String key; - - ModCountLocation(String key) { - this.key = key; - } - - public boolean isOnModsButton() { - return this == MODS_BUTTON || this == TITLE_SCREEN_AND_MODS_BUTTON; - } - - public boolean isOnTitleScreen() { - return this == TITLE_SCREEN_AND_MODS_BUTTON || this == TITLE_SCREEN; - } - - @Override - public String getKey() { - return key; - } - } - - public enum ModsButtonStyle implements OptionEnum { - INSERT("option.modmenu.mods_button_style.insert"), - ICON("option.modmenu.mods_button_style.icon"); - - final String key; - - ModsButtonStyle(String key) { - this.key = key; - } - - @Override - public String getKey() { - return key; - } - } - - public enum GameMenuButtonStyle implements OptionEnum { - INSERT("option.modmenu.game_menu_button_style.insert"), - ICON("option.modmenu.game_menu_button_style.icon"); - - final String key; - - GameMenuButtonStyle(String key) { - this.key = key; - } - - @Override - public String getKey() { - return key; - } - } - - public enum UpdateChannel implements OptionEnum { - RELEASE("option.modmenu.update_channel.release"), - BETA("option.modmenu.update_channel.beta"), - ALPHA("option.modmenu.update_channel.alpha"); - - final String key; - - UpdateChannel(String key) { - this.key = key; - } - - @Override - public String getKey() { - return key; - } - } -} +package io.github.prospector.modmenu.config; + +import com.google.gson.annotations.SerializedName; +import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.api.UpdateChannel; +import io.github.prospector.modmenu.api.UpdateInfo; +import io.github.prospector.modmenu.config.option.BooleanConfigOption; +import io.github.prospector.modmenu.config.option.EnumConfigOption; +import io.github.prospector.modmenu.config.option.OptionConvertible; +import io.github.prospector.modmenu.config.option.StringSetConfigOption; +import io.github.prospector.modmenu.util.mod.Mod; +import net.fabricmc.loader.api.ModContainer; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; + +public class ModMenuConfig { + public static final EnumConfigOption SORTING = new EnumConfigOption<>("sorting", Sorting.ASCENDING); + public static final BooleanConfigOption COUNT_LIBRARIES = new BooleanConfigOption("count_libraries", true); + public static final BooleanConfigOption COMPACT_LIST = new BooleanConfigOption("compact_list", false); + public static final BooleanConfigOption COUNT_CHILDREN = new BooleanConfigOption("count_children", true); + public static final EnumConfigOption MODS_BUTTON_STYLE = new EnumConfigOption<>("mods_button_style", TitleMenuButtonStyle.ICON); + public static final EnumConfigOption GAME_MENU_BUTTON_STYLE = new EnumConfigOption<>("game_menu_button_style", GameMenuButtonStyle.ICON); + public static final BooleanConfigOption COUNT_HIDDEN_MODS = new BooleanConfigOption("count_hidden_mods", true); + public static final EnumConfigOption MOD_COUNT_LOCATION = new EnumConfigOption<>("mod_count_location", ModCountLocation.TITLE_SCREEN); + public static final BooleanConfigOption HIDE_MOD_LINKS = new BooleanConfigOption("hide_mod_links", false); + public static final EnumConfigOption SHOW_LIBRARIES = new EnumConfigOption<>("show_libraries", LibraryVisibility.WITH_CONFIG); + public static final BooleanConfigOption HIDE_MOD_LICENSE = new BooleanConfigOption("hide_mod_license", false); + public static final BooleanConfigOption HIDE_BADGES = new BooleanConfigOption("hide_badges", false); + public static final BooleanConfigOption HIDE_MOD_CREDITS = new BooleanConfigOption("hide_mod_credits", false); + public static final BooleanConfigOption EASTER_EGGS = new BooleanConfigOption("easter_eggs", true); + public static final BooleanConfigOption RANDOM_JAVA_COLORS = new BooleanConfigOption("random_java_colors", false); + public static final BooleanConfigOption TRANSLATE_NAMES = new BooleanConfigOption("translate_names", true); + public static final BooleanConfigOption TRANSLATE_DESCRIPTIONS = new BooleanConfigOption("translate_descriptions", true); + public static final BooleanConfigOption UPDATE_CHECKER = new BooleanConfigOption("update_checker", true); + public static final BooleanConfigOption BUTTON_UPDATE_BADGE = new BooleanConfigOption("button_update_badge", true); + public static final EnumConfigOption UPDATE_CHANNEL = new EnumConfigOption<>("update_channel", UpdateChannel.RELEASE); + public static final BooleanConfigOption QUICK_CONFIGURE = new BooleanConfigOption("quick_configure", true); + + @FileOnlyConfig + public static final BooleanConfigOption MODIFY_TITLE_SCREEN = new BooleanConfigOption("modify_title_screen", true); + @FileOnlyConfig + public static final BooleanConfigOption MODIFY_GAME_MENU = new BooleanConfigOption("modify_game_menu", true); + @FileOnlyConfig + public static final BooleanConfigOption HIDE_CONFIG_BUTTONS = new BooleanConfigOption("hide_config_buttons", false); + @FileOnlyConfig + public static final BooleanConfigOption CONFIG_MODE = new BooleanConfigOption("config_mode", false); + @FileOnlyConfig + public static final BooleanConfigOption DISABLE_DRAG_AND_DROP = new BooleanConfigOption("disable_drag_and_drop", false); + @FileOnlyConfig + public static final StringSetConfigOption HIDDEN_MODS = new StringSetConfigOption("hidden_mods", new HashSet<>()); + @FileOnlyConfig + public static final StringSetConfigOption HIDDEN_CONFIGS = new StringSetConfigOption("hidden_configs", new HashSet<>()); + @FileOnlyConfig + public static final StringSetConfigOption DISABLE_UPDATE_CHECKER = new StringSetConfigOption("disable_update_checker", new HashSet<>()); + + public static List asOptions() { + List options = new ArrayList<>(); + for (Field field : ModMenuConfig.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && + OptionConvertible.class.isAssignableFrom(field.getType()) && + !field.isAnnotationPresent(FileOnlyConfig.class)) { + try { + options.add((OptionConvertible) field.get(null)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return options; + } + + public enum Sorting { + ASCENDING(Comparator.comparing(c -> c.getMetadata().getName().toLowerCase(Locale.ROOT))), + DESCENDING(ASCENDING.getComparator().reversed()), + HAS_UPDATE(Comparator.comparing((ModContainer c) -> { + Mod mod = ModMenu.MODS.get(c.getMetadata().getId()); + if (mod == null) return false; + UpdateInfo info = mod.getUpdateInfo(); + return info != null && info.isUpdateAvailable(); + }).reversed()); + + private final Comparator comparator; + + Sorting(Comparator comparator) { + this.comparator = comparator; + } + + public Comparator getComparator() { + return comparator; + } + } + + public enum LibraryVisibility { + FALSE, + WITH_CONFIG, + TRUE; + + public boolean hideMod(Mod mod, boolean hasConfig) { + boolean isLibrary = ModMenu.LIBRARY_MODS.contains(mod.getId()); + return switch (this) { + case TRUE -> false; + case FALSE -> isLibrary; + case WITH_CONFIG -> isLibrary && !hasConfig; + }; + } + + public boolean isTrue() { + return this == TRUE; + } + } + + public enum ModCountLocation { + TITLE_SCREEN(true, false), + MODS_BUTTON(false, true), + TITLE_SCREEN_AND_MODS_BUTTON(true, true), + NONE(false, false); + + private final boolean titleScreen, modsButton; + + ModCountLocation(boolean titleScreen, boolean modsButton) { + this.titleScreen = titleScreen; + this.modsButton = modsButton; + } + + public boolean isOnTitleScreen() { + return titleScreen; + } + + public boolean isOnModsButton() { + return modsButton; + } + } + + public enum TitleMenuButtonStyle { + @SerializedName(value = "insert", alternate = {"below_asset_packs"}) INSERT, + @SerializedName(value = "icon", alternate = {"replace", "replace_asset_packs"}) ICON + } + + public enum GameMenuButtonStyle { + @SerializedName(value = "insert", alternate = {"below_bugs"}) INSERT, + @SerializedName(value = "icon", alternate = {"replace", "replace_bugs"}) ICON + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java index dcff5fcd7..180c0e60f 100644 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java +++ b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigManager.java @@ -1,64 +1,133 @@ -package io.github.prospector.modmenu.config; - -import io.github.prospector.modmenu.ModMenu; -import net.fabricmc.loader.api.FabricLoader; - -import java.io.*; - -public class ModMenuConfigManager { - private static File file; - private static ModMenuConfig config; - - private static void prepareBiomeConfigFile() { - if (file != null) { - return; - } - file = new File(FabricLoader.getInstance().getConfigDir().toFile(), ModMenu.MOD_ID + ".json"); - } - - public static ModMenuConfig initializeConfig() { - if (config != null) { - return config; - } - - config = new ModMenuConfig(); - load(); - - return config; - } - - private static void load() { - prepareBiomeConfigFile(); - - try { - if (!file.exists()) { - save(); - } - if (file.exists()) { - BufferedReader br = new BufferedReader(new FileReader(file)); - - config = ModMenu.GSON.fromJson(br, ModMenuConfig.class); - } - } catch (FileNotFoundException e) { - System.err.println("Couldn't load Mod Menu configuration file; reverting to defaults"); - e.printStackTrace(); - } - } - - public static void save() { - prepareBiomeConfigFile(); - - String jsonString = ModMenu.GSON.toJson(config); - - try (FileWriter fileWriter = new FileWriter(file)) { - fileWriter.write(jsonString); - } catch (IOException e) { - System.err.println("Couldn't save Mod Menu configuration file"); - e.printStackTrace(); - } - } - - public static ModMenuConfig getConfig() { - return config; - } -} +package io.github.prospector.modmenu.config; + +import com.google.gson.*; +import io.github.prospector.modmenu.ModMenu; +import io.github.prospector.modmenu.config.option.BooleanConfigOption; +import io.github.prospector.modmenu.config.option.ConfigOptionStorage; +import io.github.prospector.modmenu.config.option.EnumConfigOption; +import io.github.prospector.modmenu.config.option.StringSetConfigOption; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +public class ModMenuConfigManager { + private static Path path; + + private static void prepareConfigPath() { + if (path == null) { + path = FabricLoader.getInstance().getConfigDir().resolve(ModMenu.MOD_ID + ".json"); + } + } + + public static void initializeConfig() { + load(); + } + + @SuppressWarnings("unchecked") + private static void load() { + prepareConfigPath(); + + try { + if (!Files.exists(path)) { + save(); + } + + if (Files.exists(path)) { + BufferedReader br = Files.newBufferedReader(path); + JsonObject json = JsonParser.parseReader(br).getAsJsonObject(); + for (Field field : ModMenuConfig.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { + if (StringSetConfigOption.class.isAssignableFrom(field.getType())) { + JsonArray jsonArray = json.getAsJsonArray(field.getName().toLowerCase(Locale.ROOT)); + if (jsonArray != null) { + StringSetConfigOption option = (StringSetConfigOption) field.get(null); + Set values = new HashSet<>(); + for (JsonElement element : jsonArray) { + values.add(element.getAsString()); + } + ConfigOptionStorage.setStringSet(option.getKey(), values); + } + } else if (BooleanConfigOption.class.isAssignableFrom(field.getType())) { + JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive(field.getName().toLowerCase(Locale.ROOT)); + if (jsonPrimitive != null && jsonPrimitive.isBoolean()) { + BooleanConfigOption option = (BooleanConfigOption) field.get(null); + ConfigOptionStorage.setBoolean(option.getKey(), jsonPrimitive.getAsBoolean()); + } + } else if (EnumConfigOption.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType) { + JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive(field.getName().toLowerCase(Locale.ROOT)); + if (jsonPrimitive != null && jsonPrimitive.isString()) { + Type generic = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; + if (generic instanceof Class) { + EnumConfigOption option = (EnumConfigOption) field.get(null); + Enum found = null; + for (Enum value : ((Class>) generic).getEnumConstants()) { + if (value.name().toLowerCase(Locale.ROOT).equals(jsonPrimitive.getAsString())) { + found = value; + break; + } + } + + if (found != null) { + ConfigOptionStorage.setEnumTypeless(option.getKey(), found); + } + } + } + } + } + } + } + } catch (IOException | IllegalAccessException e) { + System.err.println("Couldn't load Mod Menu configuration file; reverting to defaults"); + e.printStackTrace(); + } + } + + @SuppressWarnings("unchecked") + public static void save() { + ModMenu.clearModCountCache(); + prepareConfigPath(); + JsonObject config = new JsonObject(); + try { + for (Field field : ModMenuConfig.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { + if (BooleanConfigOption.class.isAssignableFrom(field.getType())) { + BooleanConfigOption option = (BooleanConfigOption) field.get(null); + config.addProperty(field.getName().toLowerCase(Locale.ROOT), ConfigOptionStorage.getBoolean(option.getKey())); + } else if (StringSetConfigOption.class.isAssignableFrom(field.getType())) { + StringSetConfigOption option = (StringSetConfigOption) field.get(null); + JsonArray array = new JsonArray(); + ConfigOptionStorage.getStringSet(option.getKey()).forEach(array::add); + config.add(field.getName().toLowerCase(Locale.ROOT), array); + } else if (EnumConfigOption.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType) { + Type generic = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; + if (generic instanceof Class) { + EnumConfigOption option = (EnumConfigOption) field.get(null); + config.addProperty(field.getName().toLowerCase(Locale.ROOT), ConfigOptionStorage.getEnumTypeless(option.getKey(), (Class>) generic).name().toLowerCase(Locale.ROOT)); + } + } + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + String jsonString = ModMenu.GSON.toJson(config); + try (BufferedWriter fileWriter = Files.newBufferedWriter(path)) { + fileWriter.write(jsonString); + } catch (IOException e) { + System.err.println("Couldn't save Mod Menu configuration file"); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java b/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java deleted file mode 100644 index be98bced0..000000000 --- a/src/main/java/io/github/prospector/modmenu/config/ModMenuConfigScreen.java +++ /dev/null @@ -1,238 +0,0 @@ -package io.github.prospector.modmenu.config; - -import io.github.prospector.modmenu.gui.ModListScreen; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ButtonElement; -import net.minecraft.client.gui.Screen; -import net.minecraft.client.render.renderer.GLRenderer; -import net.minecraft.client.render.renderer.Shaders; -import net.minecraft.client.render.tessellator.TessellatorGeneral; -import net.minecraft.core.lang.I18n; -import net.minecraft.core.sound.SoundCategory; -import org.lwjgl.input.Keyboard; -import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.GL11; - -public class ModMenuConfigScreen extends Screen { - private final Screen parent; - private float scrollPosition = 0.0F; - private int contentHeight = 0; - private boolean scrolling = false; - ModMenuConfig config = ModMenuConfigManager.getConfig(); - - public ModMenuConfigScreen(Screen parent) { - super(parent); - this.parent = parent; - } - - @Override - public void init() { - this.buttons.clear(); - - int y = 40; - y = addRow(100, config.getSortingDisplayString(), 101, config.getCountLibrariesDisplayString(), y); - y = addRow(102, config.getCompactListDisplayString(), 103, config.getCountChildrenDisplayString(), y); - y = addRow(104, config.getModsButtonStyleDisplayString(), 105, config.getGameMenuButtonStyleDisplayString(), y); - y = addRow(106, config.getCountHiddenModsDisplayString(), 107, config.getModCountLocationDisplayString(), y); - y = addRow(108, config.getHideModLinksDisplayString(), 109, config.getShowLibrariesDisplayString(), y); - y = addRow(110, config.getHideModLicenseDisplayString(), 111, config.getHideBadgesDisplayString(), y); - y = addRow(112, config.getHideModCreditsDisplayString(), 113, config.getEasterEggsDisplayString(), y); - y = addRow(114, config.getRandomJavaColorsDisplayString(), 115, config.getTranslateNamesDisplayString(), y); - y = addRow(116, config.getTranslateDescriptionsDisplayString(), 117, config.getUpdateCheckerDisplayString(), y); - y = addRow(118, config.getButtonUpdateBadgeDisplayString(), 119, config.getUpdateChannelDisplayString(), y); - y = addRow(120, config.getQuickConfigureDisplayString(), y); - - contentHeight = y + 6; - this.add(new ButtonElement(200, (this.width - 200) / 2, this.height - 32, 200, 20, I18n.getInstance().translateKey("gui.options.button.done"))); - } - - private int addRow(int id1, String t1, int y) { - int w = 150, spacing = 10; - int leftX = (this.width - (w * 2 + spacing)) / 2 - 12; - this.buttons.add(createButton(id1, leftX, y, w, t1)); - return y + 24; - } - - private int addRow(int id1, String t1, int id2, String t2, int y) { - int w = 150, spacing = 10; - int leftX = (this.width - (w * 2 + spacing)) / 2 - 12; - this.buttons.add(createButton(id1, leftX, y, w, t1)); - this.buttons.add(createButton(id2, leftX + w + spacing, y, w, t2)); - return y + 24; - } - - private ButtonElement createButton(int id, int x, int y, int w, String text) { - return new ButtonElement(id, x, y, w, 20, text) { - @Override - public void drawButton(Minecraft mc, int mx, int my) { - int ay = this.yPosition - (int) ModMenuConfigScreen.this.scrollPosition; - if (ay + this.height > 32 && ay < ModMenuConfigScreen.this.height - 40) { - boolean hovered = mx >= this.xPosition && my >= ay && mx < this.xPosition + this.width && my < ay + this.height; - boolean inBounds = my >= 32 && my <= ModMenuConfigScreen.this.height - 40; - int oy = this.yPosition; - this.yPosition = ay; - super.drawButton(mc, (hovered && !inBounds) ? -9999 : mx, (hovered && !inBounds) ? -9999 : my); - this.yPosition = oy; - } - } - }; - } - - @Override - public void mouseClicked(int mx, int my, int buttonNum) { - int top = 32, bottom = this.height - 40, h = bottom - top; - int maxScroll = Math.max(0, contentHeight - h); - int sbX = (this.width + 310) / 2 + 6; - - if (maxScroll > 0 && buttonNum == 0 && mx >= sbX && mx <= sbX + 6 && my >= top && my <= bottom) { - this.scrolling = true; - return; - } - - for (Object obj : this.buttons) { - if (obj instanceof ButtonElement b) { - if (b.id == 200) { - if (mx >= b.xPosition && my >= b.yPosition && mx < b.xPosition + b.width && my < b.yPosition + b.height) { - this.clickedButton = b; - if (b.playSound) this.mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); - buttonClicked(b); - return; - } - } else if (my >= top && my <= bottom) { - int ay = b.yPosition - (int) this.scrollPosition; - if (mx >= b.xPosition && my >= ay && mx < b.xPosition + b.width && my < ay + b.height) { - int oy = b.yPosition; - b.yPosition = ay; - boolean clicked = b.mouseClicked(this.mc, mx, my); - b.yPosition = oy; - if (clicked) { - this.clickedButton = b; - if (b.playSound) this.mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); - buttonClicked(b); - return; - } - } - } - } - } - super.mouseClicked(mx, my, buttonNum); - } - - @Override - public void mouseReleased(int mx, int my, int buttonNum) { - if (buttonNum == 0) this.scrolling = false; - super.mouseReleased(mx, my, buttonNum); - } - - @Override - public void render(int mx, int my, float delta) { - int top = 32, bottom = this.height - 40, h = bottom - top; - int maxScroll = Math.max(0, contentHeight - h); - - if (this.scrolling && maxScroll > 0) { - if (!Mouse.isButtonDown(0)) { - this.scrolling = false; - } else { - float th = Math.max(32, (h * h) / contentHeight); - float track = h - th; - if (track > 0) { - this.scrollPosition = Math.max(0, Math.min((my - top - th / 2) * maxScroll / track, maxScroll)); - } - } - } - - int wheel = Mouse.getDWheel(); - if (wheel != 0 && maxScroll > 0) { - this.scrollPosition = Math.max(0, Math.min(this.scrollPosition + (wheel > 0 ? -24 : 24), maxScroll)); - } - - this.renderBackground(); - - int scale = this.mc.resolution.getScale(); - GL11.glEnable(GL11.GL_SCISSOR_TEST); - GL11.glScissor(0, 40 * scale, this.mc.gameWindow.getWidthPixels(), h * scale); - - for (Object obj : this.buttons) { - if (obj instanceof ButtonElement b && b.id != 200) b.drawButton(this.mc, mx, my); - } - - GL11.glDisable(GL11.GL_SCISSOR_TEST); - - this.drawRect(0, 0, this.width, 32, 0x40000000); - this.drawStringCenteredNoShadow(this.fontRenderer, I18n.getInstance().translateKey("modmenu.options"), this.width / 2, 12, 0xFFFFFF); - this.drawRect(0, bottom, this.width, this.height, 0x40000000); - - for (Object obj : this.buttons) { - if (obj instanceof ButtonElement b && b.id == 200) b.drawButton(this.mc, mx, my); - } - - if (maxScroll > 0) { - int k = (this.width + 310) / 2 + 6, l = k + 6; - GLRenderer.pushFrame(); - GLRenderer.setShader(Shaders.COLOR); - TessellatorGeneral t = GLRenderer.getTessellator(); - int th = Math.max(32, (h * h) / contentHeight); - int r = Math.max(top, (int)this.scrollPosition * (h - th) / maxScroll + top); - - t.startDrawingQuads(); - t.setColorOpaque3i(0, 0, 0); - t.addVertexWithUV(k, bottom, 0, 0, 1); t.addVertexWithUV(l, bottom, 0, 1, 1); - t.addVertexWithUV(l, top, 0, 1, 0); t.addVertexWithUV(k, top, 0, 0, 0); - t.draw(); - - t.startDrawingQuads(); - t.setColorOpaque3i(128, 128, 128); - t.addVertexWithUV(k, r + th, 0, 0, 1); t.addVertexWithUV(l, r + th, 0, 1, 1); - t.addVertexWithUV(l, r, 0, 1, 0); t.addVertexWithUV(k, r, 0, 0, 0); - t.draw(); - - t.startDrawingQuads(); - t.setColorOpaque3i(192, 192, 192); - t.addVertexWithUV(k, r + th - 1, 0, 0, 1); t.addVertexWithUV(l - 1, r + th - 1, 0, 1, 1); - t.addVertexWithUV(l - 1, r, 0, 1, 0); t.addVertexWithUV(k, r, 0, 0, 0); - t.draw(); - GLRenderer.popFrame(); - } - } - - @Override - protected void buttonClicked(ButtonElement button) { - String string = null; - switch (button.id) { - case 100: config.toggleSortMode(); string = config.getSortingDisplayString(); break; - case 101: config.toggleCountLibraries(); string = config.getCountLibrariesDisplayString(); break; - case 102: config.toggleCompactList(); string = config.getCompactListDisplayString(); break; - case 103: config.toggleCountChildren(); string = config.getCountChildrenDisplayString(); break; - case 104: config.toggleModsButtonStyle(); string = config.getModsButtonStyleDisplayString(); break; - case 105: config.toggleGameMenuButtonStyle(); string = config.getGameMenuButtonStyleDisplayString(); break; - case 106: config.toggleCountHiddenMods(); string = config.getCountHiddenModsDisplayString(); break; - case 107: config.toggleModCountLocation(); string = config.getModCountLocationDisplayString(); break; - case 108: config.toggleHideModLinks(); string = config.getHideModLinksDisplayString(); break; - case 109: config.toggleShowLibraries(); string = config.getShowLibrariesDisplayString(); break; - case 110: config.toggleHideModLicense(); string = config.getHideModLicenseDisplayString(); break; - case 111: config.toggleHideBadges(); string = config.getHideBadgesDisplayString(); break; - case 112: config.toggleHideModCredits(); string = config.getHideModCreditsDisplayString(); break; - case 113: config.toggleEasterEggs(); string = config.getEasterEggsDisplayString(); break; - case 114: config.toggleRandomJavaColors(); string = config.getRandomJavaColorsDisplayString(); break; - case 115: config.toggleTranslateNames(); string = config.getTranslateNamesDisplayString(); break; - case 116: config.toggleTranslateDescriptions(); string = config.getTranslateDescriptionsDisplayString(); break; - case 117: config.toggleUpdateChecker(); string = config.getUpdateCheckerDisplayString(); break; - case 118: config.toggleButtonUpdateBadge(); string = config.getButtonUpdateBadgeDisplayString(); break; - case 119: config.toggleUpdateChannel(); string = config.getUpdateChannelDisplayString(); break; - case 120: config.toggleQuickConfigure(); string = config.getQuickConfigureDisplayString(); break; - case 200: closeAndReturn(); break; - } - if (string != null) button.displayString = string; - } - - @Override - public void keyPressed(char c, int key, int mx, int my) { - if (key == Keyboard.KEY_ESCAPE || key == Keyboard.KEY_BACK) - closeAndReturn(); - } - - private void closeAndReturn() { - if (this.parent instanceof ModListScreen p) p.getModList().reloadFilters(); - this.mc.displayScreen(this.parent); - } -} diff --git a/src/main/java/io/github/prospector/modmenu/config/option/BooleanConfigOption.java b/src/main/java/io/github/prospector/modmenu/config/option/BooleanConfigOption.java new file mode 100644 index 000000000..cf8070959 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/option/BooleanConfigOption.java @@ -0,0 +1,65 @@ +package io.github.prospector.modmenu.config.option; + +import io.github.prospector.modmenu.util.TranslationUtil; +import net.minecraft.core.lang.I18n; + +public class BooleanConfigOption implements OptionConvertible { + private final String key; + private final String translationKey; + private final boolean defaultValue; + + public BooleanConfigOption(String key, boolean defaultValue, String enabledKey, String disabledKey) { + ConfigOptionStorage.setBoolean(key, defaultValue); + this.key = key; + this.translationKey = TranslationUtil.translationKeyOf("option", key); + this.defaultValue = defaultValue; + } + + public BooleanConfigOption(String key, boolean defaultValue) { + this(key, defaultValue, "true", "false"); + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getTranslationKey() { + return translationKey; + } + + public boolean getValue() { + return ConfigOptionStorage.getBoolean(key); + } + + public void setValue(boolean value) { + ConfigOptionStorage.setBoolean(key, value); + } + + public void toggleValue() { + ConfigOptionStorage.toggleBoolean(key); + } + + public boolean getDefaultValue() { + return defaultValue; + } + + @Override + public String getButtonText() { + I18n i18n = I18n.getInstance(); + String name = i18n.translateKey(translationKey); + String value = i18n.translateKey(translationKey + (getValue() ? ".true" : ".false")); + return name + ": " + value; + } + + @Override + public void cycle(int direction) { + toggleValue(); + } + + @Override + public boolean isDefault() { + return getValue() == defaultValue; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/option/ConfigOptionStorage.java b/src/main/java/io/github/prospector/modmenu/config/option/ConfigOptionStorage.java new file mode 100644 index 000000000..802251aa7 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/option/ConfigOptionStorage.java @@ -0,0 +1,60 @@ +package io.github.prospector.modmenu.config.option; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class ConfigOptionStorage { + private static final Map BOOLEAN_OPTIONS = new HashMap<>(); + private static final Map> ENUM_OPTIONS = new HashMap<>(); + private static final Map> STRING_SET_OPTIONS = new HashMap<>(); + + public static void setStringSet(String key, Set value) { + STRING_SET_OPTIONS.put(key, value); + } + + public static Set getStringSet(String key) { + return STRING_SET_OPTIONS.get(key); + } + + public static void setBoolean(String key, boolean value) { + BOOLEAN_OPTIONS.put(key, value); + } + + public static void toggleBoolean(String key) { + setBoolean(key, !getBoolean(key)); + } + + public static boolean getBoolean(String key) { + return BOOLEAN_OPTIONS.get(key); + } + + @SuppressWarnings("unchecked") + public static > E getEnum(String key, Class typeClass) { + return (E) ENUM_OPTIONS.get(key); + } + + public static Enum getEnumTypeless(String key, Class> typeClass) { + return ENUM_OPTIONS.get(key); + } + + public static > void setEnum(String key, E value) { + ENUM_OPTIONS.put(key, value); + } + + public static void setEnumTypeless(String key, Enum value) { + ENUM_OPTIONS.put(key, value); + } + + public static > E cycleEnum(String key, Class typeClass) { + return cycleEnum(key, typeClass, 1); + } + + public static > E cycleEnum(String key, Class typeClass, int amount) { + E[] values = typeClass.getEnumConstants(); + E currentValue = getEnum(key, typeClass); + E newValue = values[(currentValue.ordinal() + values.length + amount) % values.length]; + setEnum(key, newValue); + return newValue; + } +} diff --git a/src/main/java/io/github/prospector/modmenu/config/option/EnumConfigOption.java b/src/main/java/io/github/prospector/modmenu/config/option/EnumConfigOption.java new file mode 100644 index 000000000..34e082e67 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/option/EnumConfigOption.java @@ -0,0 +1,63 @@ +package io.github.prospector.modmenu.config.option; + +import io.github.prospector.modmenu.util.TranslationUtil; +import net.minecraft.core.lang.I18n; + +import java.util.Locale; + +public class EnumConfigOption> implements OptionConvertible { + private final String key; + private final String translationKey; + private final Class enumClass; + private final E defaultValue; + + public EnumConfigOption(String key, E defaultValue) { + ConfigOptionStorage.setEnum(key, defaultValue); + this.key = key; + this.translationKey = TranslationUtil.translationKeyOf("option", key); + this.enumClass = defaultValue.getDeclaringClass(); + this.defaultValue = defaultValue; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getTranslationKey() { + return translationKey; + } + + public E getValue() { + return ConfigOptionStorage.getEnum(key, enumClass); + } + + public void setValue(E value) { + ConfigOptionStorage.setEnum(key, value); + } + + public void cycleValue(int amount) { + ConfigOptionStorage.cycleEnum(key, enumClass, amount); + } + + public String getValueText(E value) { + return I18n.getInstance().translateKey(translationKey + "." + value.name().toLowerCase(Locale.ROOT)); + } + + @Override + public String getButtonText() { + String name = I18n.getInstance().translateKey(translationKey); + return name + ": " + getValueText(getValue()); + } + + @Override + public void cycle(int direction) { + cycleValue(direction); + } + + @Override + public boolean isDefault() { + return getValue() == defaultValue; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/config/option/OptionConvertible.java b/src/main/java/io/github/prospector/modmenu/config/option/OptionConvertible.java new file mode 100644 index 000000000..c6c94bf23 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/option/OptionConvertible.java @@ -0,0 +1,13 @@ +package io.github.prospector.modmenu.config.option; + +public interface OptionConvertible { + String getKey(); + + String getTranslationKey(); + + String getButtonText(); + + void cycle(int direction); + + boolean isDefault(); +} diff --git a/src/main/java/io/github/prospector/modmenu/config/option/StringSetConfigOption.java b/src/main/java/io/github/prospector/modmenu/config/option/StringSetConfigOption.java new file mode 100644 index 000000000..314da6897 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/config/option/StringSetConfigOption.java @@ -0,0 +1,30 @@ +package io.github.prospector.modmenu.config.option; + +import io.github.prospector.modmenu.util.TranslationUtil; + +import java.util.Set; + +public class StringSetConfigOption { + private final String key; + private final String translationKey; + private final Set defaultValue; + + public StringSetConfigOption(String key, Set defaultValue) { + ConfigOptionStorage.setStringSet(key, defaultValue); + this.key = key; + this.translationKey = TranslationUtil.translationKeyOf("option", key); + this.defaultValue = defaultValue; + } + + public String getKey() { + return key; + } + + public Set getValue() { + return ConfigOptionStorage.getStringSet(key); + } + + public void setValue(Set value) { + ConfigOptionStorage.setStringSet(key, value); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java new file mode 100644 index 000000000..4203e2ab7 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java @@ -0,0 +1,231 @@ +package io.github.prospector.modmenu.gui; + +import io.github.prospector.modmenu.config.ModMenuConfig; +import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.config.option.OptionConvertible; +import io.github.prospector.modmenu.util.ButtonUtil; +import io.github.prospector.modmenu.util.UpdateCheckerUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ButtonElement; +import net.minecraft.client.gui.Screen; +import net.minecraft.client.render.renderer.GLRenderer; +import net.minecraft.client.render.renderer.Shaders; +import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.core.lang.I18n; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; +import java.util.List; + +public class ModMenuOptionsScreen extends Screen { + private final Screen parent; + private final List options = new ArrayList<>(); + private final List optionButtons = new ArrayList<>(); + private int nextButtonId = 0; + private static final int DONE_ID = 1000; + private static final int TOP = 32; + private static final int BOTTOM_MARGIN = 40; + + private float scrollPosition = 0.0F; + private int contentHeight = 0; + private boolean scrolling = false; + + public ModMenuOptionsScreen(Screen previous) { + this.parent = previous; + this.options.addAll(ModMenuConfig.asOptions()); + } + + @Override + public void init() { + I18n i18n = I18n.getInstance(); + + this.buttons.clear(); + this.optionButtons.clear(); + this.nextButtonId = 0; + this.scrollPosition = 0.0F; + this.scrolling = false; + + int buttonWidth = 150; + int buttonHeight = 20; + int gap = 4; + int columns = 2; + int rowHeight = buttonHeight + gap; + + int totalWidth = columns * buttonWidth + (columns - 1) * gap; + int startX = this.width / 2 - totalWidth / 2; + int startY = 40; + + for (int i = 0; i < options.size(); i++) { + int col = i % columns; + int row = i / columns; + int x = startX + col * (buttonWidth + gap); + int y = startY + row * rowHeight; + + OptionConvertible option = options.get(i); + final int id = nextButtonId++; + + ButtonElement button = new ButtonElement(id, x, y, buttonWidth, buttonHeight, option.getButtonText()) { + @Override + public void drawButton(Minecraft mc, int mouseX, int mouseY) { + int ay = this.yPosition - (int) ModMenuOptionsScreen.this.scrollPosition; + if (ay + this.height > TOP && ay < ModMenuOptionsScreen.this.height - BOTTOM_MARGIN) { + boolean hovered = mouseX >= this.xPosition && mouseY >= ay && mouseX < this.xPosition + this.width && mouseY < ay + this.height; + boolean inBounds = mouseY >= TOP && mouseY <= ModMenuOptionsScreen.this.height - BOTTOM_MARGIN; + int oy = this.yPosition; + this.yPosition = ay; + this.displayString = option.getButtonText(); + super.drawButton(mc, (hovered && !inBounds) ? -9999 : mouseX, (hovered && !inBounds) ? -9999 : mouseY); + this.yPosition = oy; + } + } + }; + this.buttons.add(button); + this.optionButtons.add(button); + } + + int lastRow = (options.size() - 1) / columns; + contentHeight = startY + (lastRow + 1) * rowHeight + 6; + + int doneY = this.height - 28; + this.buttons.add(ButtonUtil.createButton(DONE_ID, this.width / 2 - 75, doneY, 150, 20, i18n.translateKey("gui.options.button.done"))); + } + + @Override + public void mouseClicked(int mouseX, int mouseY, int mouseButton) { + int top = TOP, bottom = this.height - BOTTOM_MARGIN, h = bottom - top; + int maxScroll = Math.max(0, contentHeight - h); + int sbX = (this.width + 310) / 2 + 6; + + if (maxScroll > 0 && mouseButton == 0 && mouseX >= sbX && mouseX <= sbX + 6 && mouseY >= top && mouseY <= bottom) { + this.scrolling = true; + return; + } + + for (Object obj : this.buttons) { + if (obj instanceof ButtonElement b) { + if (b.id == DONE_ID) { + if (mouseX >= b.xPosition && mouseY >= b.yPosition && mouseX < b.xPosition + b.width && mouseY < b.yPosition + b.height) { + this.clickedButton = b; + buttonClicked(b); + return; + } + } else if (mouseY >= top && mouseY <= bottom) { + int ay = b.yPosition - (int) this.scrollPosition; + if (mouseX >= b.xPosition && mouseY >= ay && mouseX < b.xPosition + b.width && mouseY < ay + b.height) { + int oy = b.yPosition; + b.yPosition = ay; + boolean clicked = b.mouseClicked(this.mc, mouseX, mouseY); + b.yPosition = oy; + if (clicked) { + this.clickedButton = b; + buttonClicked(b); + return; + } + } + } + } + } + + super.mouseClicked(-9999, -9999, mouseButton); + } + + @Override + public void mouseReleased(int mouseX, int mouseY, int mouseButton) { + if (mouseButton == 0) this.scrolling = false; + super.mouseReleased(mouseX, mouseY, mouseButton); + } + + @Override + protected void buttonClicked(ButtonElement button) { + if (button.id == DONE_ID) { + this.mc.displayScreen(this.parent); + return; + } + + if (button.id >= 0 && button.id < optionButtons.size()) { + OptionConvertible option = options.get(button.id); + boolean shift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT); + option.cycle(shift ? -1 : 1); + } + } + + @Override + public void render(int mouseX, int mouseY, float delta) { + int top = TOP, bottom = this.height - BOTTOM_MARGIN, h = bottom - top; + int maxScroll = Math.max(0, contentHeight - h); + + if (this.scrolling && maxScroll > 0) { + if (!Mouse.isButtonDown(0)) { + this.scrolling = false; + } else { + float th = Math.max(32, (h * h) / (float) contentHeight); + float track = h - th; + if (track > 0) { + this.scrollPosition = Math.max(0, Math.min((mouseY - top - th / 2) * maxScroll / track, maxScroll)); + } + } + } + + int wheel = Mouse.getDWheel(); + if (wheel != 0 && maxScroll > 0) { + this.scrollPosition = Math.max(0, Math.min(this.scrollPosition + (wheel > 0 ? -24 : 24), maxScroll)); + } + + ((ModsScreen) this.parent).overlayBackground(0, 0, this.width, this.height, 64, 64, 64, 255, 255); + + int scale = this.mc.resolution.getScale(); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(0, BOTTOM_MARGIN * scale, this.mc.gameWindow.getWidthPixels(), h * scale); + + for (Object obj : this.buttons) { + if (obj instanceof ButtonElement b && b.id != DONE_ID) b.drawButton(this.mc, mouseX, mouseY); + } + + GL11.glDisable(GL11.GL_SCISSOR_TEST); + + this.drawRect(0, 0, this.width, TOP, 0x40000000); + this.drawStringCenteredNoShadow(this.fontRenderer, I18n.getInstance().translateKey("modmenu.options"), this.width / 2, 12, 0xFFFFFF); + this.drawRect(0, bottom, this.width, this.height, 0x40000000); + + for (Object obj : this.buttons) { + if (obj instanceof ButtonElement b && b.id == DONE_ID) b.drawButton(this.mc, mouseX, mouseY); + } + + if (maxScroll > 0) { + int k = (this.width + 310) / 2 + 6, l = k + 6; + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.COLOR); + TessellatorGeneral t = GLRenderer.getTessellator(); + int th = Math.max(32, (h * h) / contentHeight); + int r = Math.max(top, (int) this.scrollPosition * (h - th) / maxScroll + top); + + t.startDrawingQuads(); + t.setColorOpaque3i(0, 0, 0); + t.addVertexWithUV(k, bottom, 0, 0, 1); t.addVertexWithUV(l, bottom, 0, 1, 1); + t.addVertexWithUV(l, top, 0, 1, 0); t.addVertexWithUV(k, top, 0, 0, 0); + t.draw(); + + t.startDrawingQuads(); + t.setColorOpaque3i(128, 128, 128); + t.addVertexWithUV(k, r + th, 0, 0, 1); t.addVertexWithUV(l, r + th, 0, 1, 1); + t.addVertexWithUV(l, r, 0, 1, 0); t.addVertexWithUV(k, r, 0, 0, 0); + t.draw(); + + t.startDrawingQuads(); + t.setColorOpaque3i(192, 192, 192); + t.addVertexWithUV(k, r + th - 1, 0, 0, 1); t.addVertexWithUV(l - 1, r + th - 1, 0, 1, 1); + t.addVertexWithUV(l - 1, r, 0, 1, 0); t.addVertexWithUV(k, r, 0, 0, 0); + t.draw(); + GLRenderer.popFrame(); + } + } + + @Override + public void removed() { + super.removed(); + ModMenuConfigManager.save(); + UpdateCheckerUtil.checkForUpdates(); + } +} diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModsScreen.java similarity index 89% rename from src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java rename to src/main/java/io/github/prospector/modmenu/gui/ModsScreen.java index e920fe3b2..53f8d3310 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModsScreen.java @@ -3,6 +3,11 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.gui.widget.ModListWidget; +import io.github.prospector.modmenu.gui.widget.SmallModMenuButtonWidget; +import io.github.prospector.modmenu.gui.widget.TextFieldWidget; +import io.github.prospector.modmenu.gui.widget.DescriptionListWidget; +import io.github.prospector.modmenu.gui.widget.entries.ModListEntry; import io.github.prospector.modmenu.util.*; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.metadata.ModMetadata; @@ -27,11 +32,10 @@ import org.spongepowered.include.com.google.common.base.Joiner; import java.io.File; -import java.text.NumberFormat; import java.util.*; import java.util.stream.Collectors; -public class ModListScreen extends Screen { +public class ModsScreen extends Screen { private static final String FILTERS_BUTTON_LOCATION = "/assets/" + ModMenu.MOD_ID + "/textures/gui/filters_button.png"; private static final String CONFIGURE_BUTTON_LOCATION = "/assets/" + ModMenu.MOD_ID + "/textures/gui/configure_button.png"; private static final Logger LOGGER = LoggerFactory.getLogger("modlistscreen"); @@ -54,7 +58,6 @@ public class ModListScreen extends Screen { public Set showModChildren = new HashSet<>(); private String lastSearchString = null; - ModMenuConfig config = ModMenuConfigManager.getConfig(); I18n i18n = I18n.getInstance(); FabricLoader loader = FabricLoader.getInstance(); @@ -67,7 +70,7 @@ public class ModListScreen extends Screen { private static final int MODS_FOLDER_BUTTON_ID = 6; private static final int DONE_BUTTON_ID = 7; - public ModListScreen(Screen previousGui) { + public ModsScreen(Screen previousGui) { this.parent = previousGui; this.textTitle = i18n.translateKey("modmenu.title"); } @@ -106,11 +109,11 @@ public void init() { String oldText = this.searchBox == null ? "" : this.searchBox.getText(); this.searchBox = new TextFieldWidget(searchBoxX, 22, searchBoxWidth, 20, i18n.translateKey("modmenu.search")); // field_6451_g this.searchBox.setText(oldText); - this.modList = new ModListWidget(this.mc, paneWidth, this.height, paneY + 19, this.height - 36, config.getCompactList() ? 23 : 36, this.searchBox.getText(), this.modList, this); + this.modList = new ModListWidget(this.mc, paneWidth, this.height, paneY + 19, this.height - 36, ModMenuConfig.COMPACT_LIST.getValue() ? 23 : 36, this.searchBox.getText(), this.modList, this); this.modList.setLeftPos(0); this.descriptionListWidget = new DescriptionListWidget(this.mc, paneWidth, this.height, paneY + 60, this.height - 36, 9 + 1, this); this.descriptionListWidget.setLeftPos(rightPaneX); - ButtonElement configureButton = new ModMenuTexturedButtonWidget(CONFIGURE_BUTTON_ID, width - 24, paneY, 20, 20, 0, 0, CONFIGURE_BUTTON_LOCATION, 32, 64) { + ButtonElement configureButton = new SmallModMenuButtonWidget(CONFIGURE_BUTTON_ID, width - 24, paneY, 20, 20, 0, 0, CONFIGURE_BUTTON_LOCATION, 32, 64) { @Override public void render(Minecraft mc, int mouseX, int mouseY) { if (isHovered(mouseX, mouseY)) { @@ -148,7 +151,7 @@ public void drawButton(Minecraft mc, int var1, int var2) { super.drawButton(mc, var1, var2); } }; - this.buttons.add(new ModMenuTexturedButtonWidget(TOGGLE_FILTER_OPTIONS_BUTTON_ID, paneWidth / 2 + searchBoxWidth / 2 - 20 / 2 + 2, 22, 20, 20, 0, 0, FILTERS_BUTTON_LOCATION, 32, 64) { + this.buttons.add(new SmallModMenuButtonWidget(TOGGLE_FILTER_OPTIONS_BUTTON_ID, paneWidth / 2 + searchBoxWidth / 2 - 20 / 2 + 2, 22, 20, 20, 0, 0, FILTERS_BUTTON_LOCATION, 32, 64) { @Override public void render(Minecraft mc, int int_1, int int_2) { super.render(mc, int_1, int_2); @@ -157,24 +160,19 @@ public void render(Minecraft mc, int int_1, int int_2) { } } }); - String showLibrariesText = config.getShowLibrariesDisplayString(); - String sortingText = config.getSortingDisplayString(); - int showLibrariesWidth = fontRenderer.stringWidth(showLibrariesText) + 20; - int sortingWidth = fontRenderer.stringWidth(sortingText) + 20; - int filtersX; - int filtersWidth = showLibrariesWidth + sortingWidth + 2; - if ((filtersWidth + fontRenderer.stringWidth(i18n.translateKeyAndFormat("modmenu.showingMods", NumberFormat.getInstance().format(modList.getDisplayedCount()) + "/" + NumberFormat.getInstance().format(loader.getAllMods().size()))) + 20) >= searchBoxX + searchBoxWidth + 22) { - filtersX = paneWidth / 2 - filtersWidth / 2; - showModCount = false; - } else { - filtersX = searchBoxX + searchBoxWidth + 22 - filtersWidth + 1; - showModCount = true; - } + String showLibrariesText = ModMenuConfig.SHOW_LIBRARIES.getButtonText(); + String sortingText = ModMenuConfig.SORTING.getButtonText(); + + int filtersX = searchBoxX; + int sortingWidth = (searchBoxWidth - 2) / 2; + int showLibrariesWidth = searchBoxWidth - sortingWidth - 2; + showModCount = false; + this.buttons.add(new ButtonElement(TOGGLE_SORT_MODE_BUTTON_ID, filtersX, 45, sortingWidth, 20, sortingText) { @Override public void drawButton(Minecraft mc, int mouseX, int mouseY) { visible = enabled = filterOptionsShown; - this.displayString = config.getSortingDisplayString(); + this.displayString = ModMenuConfig.SORTING.getButtonText(); super.drawButton(mc, mouseX, mouseY); } }); @@ -182,11 +180,11 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { @Override public void drawButton(Minecraft mc, int mouseX, int mouseY) { visible = enabled = filterOptionsShown; - this.displayString = config.getShowLibrariesDisplayString(); + this.displayString = ModMenuConfig.SHOW_LIBRARIES.getButtonText(); super.drawButton(mc, mouseX, mouseY); } }); - if (!config.getHideConfigButtons()) { + if (!ModMenuConfig.HIDE_CONFIG_BUTTONS.getValue()) { this.buttons.add(configureButton); } this.buttons.add(websiteButton); @@ -238,12 +236,14 @@ protected void buttonClicked(ButtonElement button) { break; } case TOGGLE_SORT_MODE_BUTTON_ID: { - config.toggleSortMode(); + ModMenuConfig.SORTING.cycleValue(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) ? -1 : 1); + ModMenuConfigManager.save(); modList.reloadFilters(); break; } case TOGGLE_SHOW_LIBRARIES_BUTTON_ID: { - config.toggleShowLibraries(); + ModMenuConfig.SHOW_LIBRARIES.cycleValue(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) ? -1 : 1); + ModMenuConfigManager.save(); modList.reloadFilters(); break; } @@ -337,7 +337,7 @@ public void render(int mouseX, int mouseY, float delta) { super.render(mouseX, mouseY, delta); if (showModCount || !filterOptionsShown) { String showModCountString; - if (config.getShowLibraries() && modList.getDisplayedCount() == 0) { + if (ModMenuConfig.SHOW_LIBRARIES.getValue().isTrue() && modList.getDisplayedCount() == 0) { showModCountString = computeLibraryCountText(false); } else { showModCountString = computeModCountText(true, false); @@ -354,7 +354,7 @@ public void render(int mouseX, int mouseY, float delta) { GLRenderer.setColor4f(1, 1, 1, 1); if ("java".equals(id)) { - DrawingUtil.drawRandomVersionBackground(selectedEntry.container, x, paneY, 32, 32); + DrawingUtil.drawRandomVersionBackground(selectedEntry.getContainer(), x, paneY, 32, 32); } this.selected.bindIconTexture(); @@ -364,7 +364,7 @@ public void render(int mouseX, int mouseY, float delta) { String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; - if (!translatedName.equals(translationKey) && config.getTranslateNames()) { + if (!translatedName.equals(translationKey) && ModMenuConfig.TRANSLATE_NAMES.getValue()) { name = translatedName; } else { name = metadata.getName(); @@ -378,7 +378,7 @@ public void render(int mouseX, int mouseY, float delta) { setTooltip(i18n.translateKeyAndFormat("modmenu.modIdToolTip", metadata.getId())); } if (init || badgeRenderer == null || badgeRenderer.getMetadata() != metadata) { - badgeRenderer = new BadgeRenderer(mc, x + imageOffset + fontRenderer.stringWidth(trimmedName) + 2, paneY, width - 28, selectedEntry.container, this); + badgeRenderer = new BadgeRenderer(mc, x + imageOffset + fontRenderer.stringWidth(trimmedName) + 2, paneY, width - 28, selectedEntry.getContainer(), this); init = false; } badgeRenderer.draw(mouseX, mouseY); @@ -413,7 +413,7 @@ public void render(int mouseX, int mouseY, float delta) { GLRenderer.popFrame(); } - static String getString(FontRenderer fontRenderer, String name, String trimmedName, int maxNameWidth) { + public static String getString(FontRenderer fontRenderer, String name, String trimmedName, int maxNameWidth) { if (fontRenderer.stringWidth(name) > maxNameWidth) { int maxWidth = maxNameWidth - fontRenderer.stringWidth("..."); trimmedName = ""; @@ -484,7 +484,7 @@ private String computeModCountText(boolean includeLibs, boolean onInit) { int[] rootMods = formatModCount(rootModIds, onInit); - if (includeLibs && !onInit && config.getShowLibraries()) { + if (includeLibs && !onInit && ModMenuConfig.SHOW_LIBRARIES.getValue().isTrue()) { Set rootLibIds = loader.getAllMods().stream() .map(container -> container.getMetadata().getId()) .filter(ModMenu.LIBRARY_MODS::contains) @@ -501,7 +501,7 @@ private String computeModCountText(boolean includeLibs, boolean onInit) { } private String computeLibraryCountText(boolean onInit) { - if (!onInit && config.getShowLibraries()) { + if (!onInit && ModMenuConfig.SHOW_LIBRARIES.getValue().isTrue()) { Set rootLibIds = loader.getAllMods().stream() .map(container -> container.getMetadata().getId()) .filter(ModMenu.LIBRARY_MODS::contains) diff --git a/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/AlwaysSelectedEntryListWidget.java similarity index 89% rename from src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/AlwaysSelectedEntryListWidget.java index a636855a1..0421660eb 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/AlwaysSelectedEntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/AlwaysSelectedEntryListWidget.java @@ -1,5 +1,6 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget; +import io.github.prospector.modmenu.gui.widget.entries.EntryListWidget; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; diff --git a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java similarity index 93% rename from src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java index 92483687e..d4dd57b72 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java @@ -1,9 +1,11 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget; import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.api.UpdateInfo; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.gui.ModsScreen; +import io.github.prospector.modmenu.gui.widget.entries.EntryListWidget; +import io.github.prospector.modmenu.gui.widget.entries.ModListEntry; import io.github.prospector.modmenu.util.RenderUtils; import io.github.prospector.modmenu.util.mod.Mod; import net.fabricmc.loader.api.metadata.ContactInformation; @@ -20,13 +22,11 @@ import java.util.Map; public class DescriptionListWidget extends EntryListWidget { - private final ModListScreen parent; + private final ModsScreen parent; private final FontRenderer textRenderer; private ModListEntry lastSelected = null; - ModMenuConfig config = ModMenuConfigManager.getConfig(); - - public DescriptionListWidget(Minecraft client, int width, int height, int top, int bottom, int entryHeight, ModListScreen parent) { + public DescriptionListWidget(Minecraft client, int width, int height, int top, int bottom, int entryHeight, ModsScreen parent) { super(client, width, height, top, bottom, entryHeight); this.parent = parent; this.textRenderer = client.font; @@ -120,7 +120,7 @@ public void render(int mouseX, int mouseY, float delta) { String translatedDesc = i18n.translateKey(descKey); String description; - if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { + if (!translatedDesc.equals(descKey) && ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue()) { description = translatedDesc; } else { description = selectedEntry.getMetadata().getDescription(); @@ -155,7 +155,7 @@ public void render(int mouseX, int mouseY, float delta) { links.remove("homepage"); links.remove("issues"); - if (config.getUpdateChecker()) { + if (ModMenuConfig.UPDATE_CHECKER.getValue()) { Mod mod = ModMenu.MODS.get(id); if (mod != null) { UpdateInfo updateInfo = mod.getUpdateInfo(); @@ -194,7 +194,7 @@ public void render(int mouseX, int mouseY, float delta) { } } - if (!links.isEmpty() && !config.getHideModLinks()) { + if (!links.isEmpty() && !ModMenuConfig.HIDE_MOD_LINKS.getValue()) { children().add(new DescriptionEntry("")); for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, i18n.translateKey("modmenu.links"), getRowWidth())) { @@ -217,7 +217,7 @@ public void render(int mouseX, int mouseY, float delta) { }); } - if (!licenses.isEmpty() && !config.getHideModLicense()) { + if (!licenses.isEmpty() && !ModMenuConfig.HIDE_MOD_LICENSE.getValue()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.license"))); for (String license : licenses) { @@ -225,7 +225,7 @@ public void render(int mouseX, int mouseY, float delta) { } } - if ("minecraft".equals(id) && !config.getHideModCredits()) { + if ("minecraft".equals(id) && !ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); String viewCreditsText = i18n.translateKey("modmenu.viewCredits"); for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(textRenderer, viewCreditsText, getRowWidth() - 16)) { @@ -233,7 +233,7 @@ public void render(int mouseX, int mouseY, float delta) { } } - if ((!authors.isEmpty() || !contributors.isEmpty()) && !config.getHideModCredits()) { + if ((!authors.isEmpty() || !contributors.isEmpty()) && !ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { if (!children().isEmpty()) children().add(new DescriptionEntry("")); children().add(new DescriptionEntry(i18n.translateKey("modmenu.credits"))); diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/ModListWidget.java similarity index 91% rename from src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/ModListWidget.java index 06e9a95cf..5a3977f53 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/ModListWidget.java @@ -1,18 +1,18 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget; import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.entries.ChildEntry; -import io.github.prospector.modmenu.gui.entries.IndependentEntry; -import io.github.prospector.modmenu.gui.entries.ParentEntry; +import io.github.prospector.modmenu.gui.widget.entries.ModListEntry; +import io.github.prospector.modmenu.gui.ModsScreen; +import io.github.prospector.modmenu.gui.widget.entries.ChildEntry; +import io.github.prospector.modmenu.gui.widget.entries.IndependentEntry; +import io.github.prospector.modmenu.gui.widget.entries.ParentEntry; import io.github.prospector.modmenu.util.ModListSearch; import io.github.prospector.modmenu.util.TestModContainer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Screen; import net.minecraft.client.render.renderer.GLRenderer; import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.tessellator.TessellatorGeneral; @@ -26,7 +26,7 @@ public class ModListWidget extends AlwaysSelectedEntryListWidget i public static final boolean DEBUG = Boolean.getBoolean("modmenu.debug"); private final Map modIconsCache = new HashMap<>(); - private final ModListScreen parent; + private final ModsScreen parent; private List modContainerList = null; private Set addedMods = new HashSet<>(); private String selectedModId = null; @@ -35,9 +35,7 @@ public class ModListWidget extends AlwaysSelectedEntryListWidget i private double scrollBarGrabbedY = 0; private double scrollBarGrabOffset = 0; - ModMenuConfig config = ModMenuConfigManager.getConfig(); - - public ModListWidget(Minecraft client, int width, int height, int y1, int y2, int entryHeight, String searchTerm, ModListWidget list, ModListScreen parent) { + public ModListWidget(Minecraft client, int width, int height, int y1, int y2, int entryHeight, String searchTerm, ModListWidget list, ModsScreen parent) { super(client, width, height, y1, y2, entryHeight); this.parent = parent; if (list != null) { @@ -77,10 +75,10 @@ protected boolean isSelectedItem(int index) { @Override public int addEntry(ModListEntry entry) { - if (addedMods.contains(entry.container)) { + if (addedMods.contains(entry.getContainer())) { return 0; } - addedMods.add(entry.container); + addedMods.add(entry.getContainer()); int i = super.addEntry(entry); if (entry.getMetadata().getId().equals(selectedModId)) { setSelected(entry); @@ -90,13 +88,13 @@ public int addEntry(ModListEntry entry) { @Override protected boolean removeEntry(ModListEntry entry) { - addedMods.remove(entry.container); + addedMods.remove(entry.getContainer()); return super.removeEntry(entry); } @Override protected ModListEntry remove(int index) { - addedMods.remove(getEntry(index).container); + addedMods.remove(getEntry(index).getContainer()); return super.remove(index); } @@ -138,7 +136,7 @@ private boolean hasVisibleChildMods(ModContainer parent) { if (children == null) return false; return !children.stream().allMatch(child -> { String childId = child.getMetadata().getId(); - return ModMenu.LIBRARY_MODS.contains(childId) && !config.getShowLibraries(); + return ModMenu.LIBRARY_MODS.contains(childId) && !ModMenuConfig.SHOW_LIBRARIES.getValue().isTrue(); }); } @@ -155,7 +153,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { if (this.modContainerList == null || refresh) { this.modContainerList = new ArrayList<>(); modContainerList.addAll(mods); - this.modContainerList.sort(config.getSorting().getComparator()); + this.modContainerList.sort(ModMenuConfig.SORTING.getValue().getComparator()); } List matched = ModListSearch.search(parent, searchTerm, modContainerList); @@ -166,7 +164,7 @@ private void filter(String searchTerm, boolean refresh, boolean search) { boolean library = ModMenu.LIBRARY_MODS.contains(id); //Hide parent lib mods when the config is set to hide - if (library && !config.getShowLibraries()) { + if (library && !ModMenuConfig.SHOW_LIBRARIES.getValue().isTrue()) { continue; } @@ -174,17 +172,13 @@ private void filter(String searchTerm, boolean refresh, boolean search) { if (isParent(id) && hasVisibleChildMods(container)) { //Add parent mods when not searching List children = getChildrenFor(id); - children.sort(config.getSorting().getComparator()); + children.sort(ModMenuConfig.SORTING.getValue().getComparator()); ParentEntry parent = new ParentEntry(minecraft, container, children, this); this.addEntry(parent); //Add children if they are meant to be shown if (this.parent.showModChildren.contains(id)) { List validChildren = ModListSearch.search(this.parent, searchTerm, children); for (ModContainer child : validChildren) { - String childId = child.getMetadata().getId(); - if (ModMenu.LIBRARY_MODS.contains(childId) && !config.getShowLibraries()) { - continue; - } this.addEntry(new ChildEntry(minecraft, child, parent, this, validChildren.indexOf(child) == validChildren.size() - 1)); } } @@ -266,7 +260,7 @@ public void mouseClicked(int mouseX, int mouseY, int mouseButton) { if (this.isMouseOver(mouseX, mouseY)) { ModListEntry entry = this.getEntryAtPos(mouseX, mouseY); if (entry != null) { - if (entry.list.getFocused() == null || !entry.list.getFocused().equals(entry)) { + if (entry.getList().getFocused() == null || !entry.getList().getFocused().equals(entry)) { this.setFocused(entry); this.setSelected(entry); this.setDragging(true); @@ -395,7 +389,7 @@ public int getTop() { return this.top; } - public ModListScreen getParent() { + public ModsScreen getParent() { return parent; } @@ -413,11 +407,11 @@ public void close() { this.children().forEach(ModListEntry::deleteTexture); } - BufferedImage getCachedModIcon(Path path) { + public BufferedImage getCachedModIcon(Path path) { return this.modIconsCache.get(path); } - void cacheModIcon(Path path, BufferedImage tex) { + public void cacheModIcon(Path path, BufferedImage tex) { this.modIconsCache.put(path, tex); } diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/ModMenuButtonWidget.java similarity index 73% rename from src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/ModMenuButtonWidget.java index 530203bf3..7dc182769 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/ModMenuButtonWidget.java @@ -1,7 +1,7 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget; import io.github.prospector.modmenu.ModMenu; -import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.config.ModMenuConfig; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; @@ -13,7 +13,7 @@ public ModMenuButtonWidget(int id, int x, int y, int width, int height, String t @Override public void drawButton(Minecraft minecraft, int mouseX, int mouseY) { super.drawButton(minecraft, mouseX, mouseY); - if (ModMenuConfigManager.getConfig().getButtonUpdateBadge() && ModMenu.areModUpdatesAvailable()) { + if (ModMenuConfig.BUTTON_UPDATE_BADGE.getValue() && ModMenu.areModUpdatesAvailable()) { UpdateAvailableBadge.renderBadge( this.xPosition + this.width - 13, this.yPosition + (this.height / 2) - 4 diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/SmallModMenuButtonWidget.java similarity index 82% rename from src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/SmallModMenuButtonWidget.java index 9a8d0daa2..0690355c7 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuTexturedButtonWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/SmallModMenuButtonWidget.java @@ -1,7 +1,7 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget; import io.github.prospector.modmenu.ModMenu; -import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.config.ModMenuConfig; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.render.font.FontRenderer; @@ -10,22 +10,22 @@ import net.minecraft.client.render.tessellator.TessellatorGeneral; import net.minecraft.client.render.window.CursorShape; -public class ModMenuTexturedButtonWidget extends ButtonElement { +public class SmallModMenuButtonWidget extends ButtonElement { private final String texture; private final int u; private final int v; private final int uWidth; private final int vHeight; - protected ModMenuTexturedButtonWidget(int buttonId, int x, int y, int width, int height, int u, int v, String texture) { + protected SmallModMenuButtonWidget(int buttonId, int x, int y, int width, int height, int u, int v, String texture) { this(buttonId, x, y, width, height, u, v, texture, 256, 256); } - protected ModMenuTexturedButtonWidget(int buttonId, int x, int y, int width, int height, int u, int v, String texture, int uWidth, int vHeight) { + protected SmallModMenuButtonWidget(int buttonId, int x, int y, int width, int height, int u, int v, String texture, int uWidth, int vHeight) { this(buttonId, x, y, width, height, u, v, texture, uWidth, vHeight, ""); } - protected ModMenuTexturedButtonWidget(int buttonId, int x, int y, int width, int height, int u, int v, String texture, int uWidth, int vHeight, String message) { + protected SmallModMenuButtonWidget(int buttonId, int x, int y, int width, int height, int u, int v, String texture, int uWidth, int vHeight, String message) { super(buttonId, x, y, width, height, message); this.uWidth = uWidth; this.vHeight = vHeight; @@ -83,7 +83,7 @@ public void render(Minecraft mc, int mouseX, int mouseY) { GLRenderer.popFrame(); if (this.id == 1000) { - if (ModMenuConfigManager.getConfig().getButtonUpdateBadge() && ModMenu.areModUpdatesAvailable()) { + if (ModMenuConfig.BUTTON_UPDATE_BADGE.getValue() && ModMenu.areModUpdatesAvailable()) { UpdateAvailableBadge.renderBadge( this.xPosition + this.width - 5, this.yPosition - 3 diff --git a/src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java similarity index 99% rename from src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java index 25b97491c..64ad0a3cd 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/TextFieldWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java @@ -1,4 +1,4 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget; import io.github.prospector.modmenu.mixin.MinecraftAccessor; import io.github.prospector.modmenu.mixin.TextFieldEditorAccessor; @@ -431,9 +431,9 @@ public void mouseClicked(int mouseX, int mouseY, int mouseButton) { String s = trimStringToWidth(fontRenderer, text.substring(lineScrollOffset), getWidth()); setCursorPosition(trimStringToWidth(fontRenderer, s, i).length() + lineScrollOffset); //return true; - } else { - //return false; - } + } /*else { + return false; + }*/ } /** @@ -749,5 +749,4 @@ private static String trimStringToWidth(FontRenderer font, String text, int maxW width += font.stringWidth(Character.toString(text.charAt(reverse ? text.length() - 1 - length : length))); return reverse ? text.substring(text.length() - length) : text.substring(0, length); } - } diff --git a/src/main/java/io/github/prospector/modmenu/gui/widget/UpdateAvailableBadge.java b/src/main/java/io/github/prospector/modmenu/gui/widget/UpdateAvailableBadge.java new file mode 100644 index 000000000..839c29e91 --- /dev/null +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/UpdateAvailableBadge.java @@ -0,0 +1,40 @@ +package io.github.prospector.modmenu.gui.widget; + +import io.github.prospector.modmenu.ModMenu; +import net.minecraft.client.Minecraft; +import net.minecraft.client.render.tessellator.TessellatorGeneral; +import net.minecraft.client.render.renderer.GLRenderer; +import net.minecraft.client.render.renderer.Shaders; +import net.minecraft.client.render.renderer.State; +import org.lwjgl.opengl.GL11; + +public class UpdateAvailableBadge { + private static final String UPDATE_ICON = "/assets/" + ModMenu.MOD_ID + "/textures/gui/update_available.png"; + + public static void renderBadge(int x, int y) { + GLRenderer.pushFrame(); + GLRenderer.setShader(Shaders.INTERFACE); + GLRenderer.enableState(State.BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GLRenderer.setColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + Minecraft.getMinecraft().textureManager.bindTexture(Minecraft.getMinecraft().textureManager.loadTexture(UPDATE_ICON)); + + int totalFrames = 6; + int frameHeight = 8; + int currentFrame = (int) (System.currentTimeMillis() / 200) % totalFrames; + + float vMin = (float) (currentFrame * frameHeight) / (totalFrames * frameHeight); + float vMax = (float) ((currentFrame + 1) * frameHeight) / (totalFrames * frameHeight); + + TessellatorGeneral t = GLRenderer.getTessellator(); + t.startDrawingQuads(); + t.addVertexWithUV(x, y + 8, 0, 0.0F, vMax); + t.addVertexWithUV(x + 8, y + 8, 0, 1.0F, vMax); + t.addVertexWithUV(x + 8, y, 0, 1.0F, vMin); + t.addVertexWithUV(x, y, 0, 0.0F, vMin); + t.draw(); + + GLRenderer.popFrame(); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ChildEntry.java similarity index 88% rename from src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/entries/ChildEntry.java index 96dcbf400..985f8a2a7 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ChildEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ChildEntry.java @@ -1,7 +1,6 @@ -package io.github.prospector.modmenu.gui.entries; +package io.github.prospector.modmenu.gui.widget.entries; -import io.github.prospector.modmenu.gui.ModListEntry; -import io.github.prospector.modmenu.gui.ModListWidget; +import io.github.prospector.modmenu.gui.widget.ModListWidget; import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; import org.lwjgl.input.Keyboard; diff --git a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/EntryListWidget.java similarity index 99% rename from src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/entries/EntryListWidget.java index d40cd7c43..3789550bd 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/EntryListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/EntryListWidget.java @@ -1,4 +1,4 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget.entries; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -271,7 +271,7 @@ protected void centerScrollOn(E entry) { this.setScrollAmount(this.children().indexOf(entry) * this.itemHeight + this.itemHeight / 2 - (this.bottom - this.top) / 2); } - protected void ensureVisible(E entry) { + public void ensureVisible(E entry) { int i = this.getRowTop(this.children().indexOf(entry)); int j = i - this.top - 4 - this.itemHeight; if (j < 0) { @@ -302,7 +302,7 @@ public void setScrollAmount(double d) { this.scrollAmount = d; } - private int getMaxScroll() { + protected int getMaxScroll() { return Math.max(0, this.getMaxPosition() - (this.bottom - this.top - 4)); } diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/IndependentEntry.java similarity index 62% rename from src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/entries/IndependentEntry.java index 3e2328758..082cacec7 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/IndependentEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/IndependentEntry.java @@ -1,7 +1,6 @@ -package io.github.prospector.modmenu.gui.entries; +package io.github.prospector.modmenu.gui.widget.entries; -import io.github.prospector.modmenu.gui.ModListEntry; -import io.github.prospector.modmenu.gui.ModListWidget; +import io.github.prospector.modmenu.gui.widget.ModListWidget; import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java similarity index 85% rename from src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java index 206b89f81..258667d23 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java @@ -1,10 +1,12 @@ -package io.github.prospector.modmenu.gui; +package io.github.prospector.modmenu.gui.widget.entries; import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.api.UpdateInfo; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.entries.ParentEntry; +import io.github.prospector.modmenu.gui.ModsScreen; +import io.github.prospector.modmenu.gui.widget.AlwaysSelectedEntryListWidget; +import io.github.prospector.modmenu.gui.widget.ModListWidget; +import io.github.prospector.modmenu.gui.widget.UpdateAvailableBadge; import io.github.prospector.modmenu.util.*; import io.github.prospector.modmenu.util.mod.Mod; import net.fabricmc.loader.api.FabricLoader; @@ -38,12 +40,10 @@ public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry= x && mouseY >= y && mouseX < x + rowWidth && mouseY < y + iconSize; if (hovered) { @@ -98,7 +106,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou client.textureManager.bindTexture(client.textureManager.loadTexture("/assets/modmenu/textures/gui/mod_configuration.png")); - float maxUV = config.getCompactList() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; + float maxUV = ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; float vMin = hoveringIcon ? iconSize / maxUV : 0.0f; float vMax = hoveringIcon ? (iconSize * 2) / maxUV : iconSize / maxUV; float uMax = iconSize / maxUV; @@ -122,20 +130,20 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou String translationKey = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(translationKey); String name; - if (!translatedName.equals(translationKey) && config.getTranslateNames()) { + if (!translatedName.equals(translationKey) && ModMenuConfig.TRANSLATE_NAMES.getValue()) { name = translatedName; } else { name = metadata.getName(); } String trimmedName = name; int maxNameWidth = rowWidth - iconSize - 3; - trimmedName = ModListScreen.getString(font, name, trimmedName, maxNameWidth); + trimmedName = ModsScreen.getString(font, name, trimmedName, maxNameWidth); this.drawStringNoShadow(font, trimmedName, x + iconSize + 3, y + 1, 0xFFFFFF); int badgeX = x + iconSize + 3 + font.stringWidth(trimmedName) + 2; int updateBadgeOffset = 0; - if (config.getUpdateChecker() && config.getButtonUpdateBadge()) { + if (ModMenuConfig.UPDATE_CHECKER.getValue() && ModMenuConfig.BUTTON_UPDATE_BADGE.getValue()) { Mod modInstance = ModMenu.MODS.get(id); if (modInstance != null) { UpdateInfo updateInfo = modInstance.getUpdateInfo(); @@ -149,7 +157,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou new BadgeRenderer(client, badgeX + updateBadgeOffset, y, x + rowWidth, container, list.getParent()).draw(mouseX, mouseY); int lineHeight = 9; - if (config.getCompactList()) { + if (ModMenuConfig.COMPACT_LIST.getValue()) { String versionString = "minecraft".equals(id) ? Global.VERSION : metadata.getVersion().getFriendlyString(); String prefixedVersion = VersionUtil.getPrefixedVersion(versionString); RenderUtils.INSTANCE.drawWrappedString(this, prefixedVersion, x + iconSize + 3, y + lineHeight + 2, rowWidth - iconSize - 7, 2, 0x808080); @@ -162,9 +170,9 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou String sumKey = "modmenu.summaryTranslation." + id; String translatedSum = i18n.translateKey(sumKey); - if (!translatedSum.equals(sumKey) && config.getTranslateDescriptions()) { + if (!translatedSum.equals(sumKey) && ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue()) { description = translatedSum; - } else if (!translatedDesc.equals(descKey) && config.getTranslateDescriptions()) { + } else if (!translatedDesc.equals(descKey) && ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue()) { description = translatedDesc; } else { description = metadata.getDescription(); @@ -201,7 +209,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou GLRenderer.popFrame(); } - static void internalRender(int y, int x, int iconSize) { + public static void internalRender(int y, int x, int iconSize) { GLRenderer.pushFrame(); GLRenderer.setShader(Shaders.INTERFACE); GLRenderer.enableState(State.BLEND); @@ -260,10 +268,10 @@ private BufferedImage createIcon() { public void mouseClicked(int v, int v1, int i) { list.select(this); - if (i == 0 && !(this instanceof ParentEntry) && config.getQuickConfigure()) { + if (i == 0 && !(this instanceof ParentEntry) && ModMenuConfig.QUICK_CONFIGURE.getValue()) { String id = metadata.getId(); if (ModMenu.hasConfigScreenFactory(id) || ModMenu.hasLegacyConfigScreenTask(id)) { - int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; int iconLeft = list.getRowLeft() + getXOffset(); if (v >= iconLeft && v < iconLeft + iconSize) { final Screen screen = ModMenu.getConfigScreen(id, list.getParent()); diff --git a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ParentEntry.java similarity index 90% rename from src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java rename to src/main/java/io/github/prospector/modmenu/gui/widget/entries/ParentEntry.java index 340b88c73..48f64b554 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/entries/ParentEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ParentEntry.java @@ -1,10 +1,8 @@ -package io.github.prospector.modmenu.gui.entries; +package io.github.prospector.modmenu.gui.widget.entries; import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.ModListEntry; -import io.github.prospector.modmenu.gui.ModListWidget; +import io.github.prospector.modmenu.gui.widget.ModListWidget; import io.github.prospector.modmenu.util.ModListSearch; import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; @@ -24,7 +22,6 @@ public class ParentEntry extends ModListEntry { protected ModListWidget list; protected boolean hoveringIcon = false; protected long sinceLastClick = 0; - ModMenuConfig config = ModMenuConfigManager.getConfig(); public ParentEntry(Minecraft mc, ModContainer parent, List children, ModListWidget list) { super(mc, parent, list); @@ -46,7 +43,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou if (childrenBadgeWidth < childrenWidth + 4) { childrenBadgeWidth = childrenWidth + 4; } - int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; int childrenBadgeX = x + iconSize - childrenBadgeWidth; int childrenBadgeY = y + iconSize - childrenBadgeHeight; @@ -75,7 +72,7 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou int xOffset = list.getParent().showModChildren.contains(getMetadata().getId()) ? iconSize : 0; int yOffset = hoveringIcon ? iconSize : 0; - float maxUV = config.getCompactList() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; + float maxUV = ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; this.client.textureManager.loadTexture(PARENT_MOD_TEXTURE).bind(); TessellatorGeneral t = GLRenderer.getTessellator(); @@ -92,12 +89,12 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou @Override public void mouseClicked(int mouseX, int mouseY, int i) { - int iconSize = config.getCompactList() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; int iconLeft = list.getRowLeft() + getXOffset(); if (mouseX >= iconLeft && mouseX < iconLeft + iconSize) { toggleChildren(); return; - } else if (!config.getQuickConfigure() && System.currentTimeMillis() - sinceLastClick < 250) { + } else if (!ModMenuConfig.QUICK_CONFIGURE.getValue() && System.currentTimeMillis() - sinceLastClick < 250) { toggleChildren(); return; } diff --git a/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java b/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java index 9c8dea65f..d57f10fbf 100644 --- a/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java +++ b/src/main/java/io/github/prospector/modmenu/impl/ModMenuApiImpl.java @@ -2,11 +2,11 @@ import io.github.prospector.modmenu.api.ModMenuApi; import io.github.prospector.modmenu.api.ConfigScreenFactory; -import io.github.prospector.modmenu.config.ModMenuConfigScreen; +import io.github.prospector.modmenu.gui.ModMenuOptionsScreen; public class ModMenuApiImpl implements ModMenuApi { @Override public ConfigScreenFactory getModConfigScreenFactory() { - return ModMenuConfigScreen::new; + return ModMenuOptionsScreen::new; } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java index e4cca8b93..6ef598e54 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenMainMenuMixin.java @@ -2,10 +2,9 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.ModListScreen; -import io.github.prospector.modmenu.gui.ModMenuButtonWidget; -import io.github.prospector.modmenu.gui.ModMenuTexturedButtonWidget; +import io.github.prospector.modmenu.gui.ModsScreen; +import io.github.prospector.modmenu.gui.widget.ModMenuButtonWidget; +import io.github.prospector.modmenu.gui.widget.SmallModMenuButtonWidget; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.ScreenMainMenu; @@ -27,11 +26,10 @@ public class ScreenMainMenuMixin { public void modmenu$drawMenuButton(CallbackInfo info) { ScreenMainMenu menu = (ScreenMainMenu) (Object) this; ButtonElement texturePackButton = menu.buttons.get(2); - ModMenuConfig config = ModMenuConfigManager.getConfig(); - if (config.getEasterEggs()) { + if (ModMenuConfig.EASTER_EGGS.getValue()) { texturePackButton.displayString = new Random().nextInt(1000) == 0 ? "Twin Peaks" : i18n.translateKey("gui.main_menu.button.asset_packs"); } - if (config.getModsButtonStyle() == ModMenuConfig.ModsButtonStyle.INSERT) { + if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.INSERT) { int index = menu.buttons.indexOf(texturePackButton); if (index != -1) { String buttonText = ModMenu.createModsButtonText(true); @@ -41,7 +39,7 @@ public class ScreenMainMenuMixin { } } else { menu.buttons.removeIf(b -> b.id == 11); - menu.buttons.add(new ModMenuTexturedButtonWidget( + menu.buttons.add(new SmallModMenuButtonWidget( 1000, menu.width / 2 - 124, menu.height / 4 + 132, 20, 20, 0, 0, "/assets/" + ModMenu.MOD_ID + "/textures/gui/open_button.png", 32, 64 ) {}); @@ -51,21 +49,20 @@ public class ScreenMainMenuMixin { @Inject(method = "buttonClicked", at = @At("HEAD")) private void modmenu$onActionPerformed(ButtonElement button, CallbackInfo ci) { if (button.id == 1000) { - Minecraft.getMinecraft().displayScreen(new ModListScreen((ScreenMainMenu) (Object) this)); + Minecraft.getMinecraft().displayScreen(new ModsScreen((ScreenMainMenu) (Object) this)); } } @Inject(method = "render", at = @At("RETURN")) private void modmenu$renderModCount(int mx, int my, float partialTick, CallbackInfo ci) { - ModMenuConfig config = ModMenuConfigManager.getConfig(); - if (config.getModCountLocation().isOnTitleScreen()) { + if (ModMenuConfig.MOD_COUNT_LOCATION.getValue().isOnTitleScreen()) { ScreenMainMenu menu = (ScreenMainMenu) (Object) this; String count = ModMenu.getDisplayedModCount(); String specificKey = "modmenu.mods." + count; String nKey = i18n.translateKey(specificKey); String key = !nKey.equals(specificKey) ? specificKey : "modmenu.mods.n"; String secretKey = specificKey + ".secret"; - if (config.getEasterEggs() && !i18n.translateKey(secretKey).equals(secretKey)) { + if (ModMenuConfig.EASTER_EGGS.getValue() && !i18n.translateKey(secretKey).equals(secretKey)) { key = secretKey; } String versionText = "Better than Adventure! " + Minecraft.VERSION; diff --git a/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java b/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java index 8e4c0144e..c928a5b0d 100644 --- a/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java +++ b/src/main/java/io/github/prospector/modmenu/mixin/ScreenPauseMixin.java @@ -2,10 +2,9 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.ModListScreen; -import io.github.prospector.modmenu.gui.ModMenuButtonWidget; -import io.github.prospector.modmenu.gui.ModMenuTexturedButtonWidget; +import io.github.prospector.modmenu.gui.ModsScreen; +import io.github.prospector.modmenu.gui.widget.ModMenuButtonWidget; +import io.github.prospector.modmenu.gui.widget.SmallModMenuButtonWidget; import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; import net.minecraft.client.gui.ScreenPause; @@ -18,12 +17,12 @@ public class ScreenPauseMixin extends Screen { @Inject(at = @At("RETURN"), method = "init") public void modmenu$drawMenuButton(CallbackInfo info) { - if (ModMenuConfigManager.getConfig().getGameMenuButtonStyle() == ModMenuConfig.GameMenuButtonStyle.INSERT) { + if (ModMenuConfig.GAME_MENU_BUTTON_STYLE.getValue() == ModMenuConfig.GameMenuButtonStyle.INSERT) { String buttonText = ModMenu.createModsButtonText(false); this.buttons.add(new ModMenuButtonWidget(1000, this.width / 2 - 100, this.height / 4 + 72 - 16, 200, 20, buttonText)); } else { this.buttons.removeIf(b -> b.id == 10); - this.buttons.add(new ModMenuTexturedButtonWidget( + this.buttons.add(new SmallModMenuButtonWidget( 1000, this.width / 2 - 124, this.height / 4 + 104, 20, 20, 0, 0, "/assets/" + ModMenu.MOD_ID + "/textures/gui/open_button.png", 32, 64 ) {}); @@ -33,7 +32,7 @@ public class ScreenPauseMixin extends Screen { @Inject(method = "buttonClicked", at = @At("HEAD")) private void modmenu$onActionPerformed(ButtonElement button, CallbackInfo ci) { if (button.id == 1000) { - this.mc.displayScreen(new ModListScreen(this)); + this.mc.displayScreen(new ModsScreen(this)); } } } diff --git a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java index ed130a0a8..f5a314909 100644 --- a/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java +++ b/src/main/java/io/github/prospector/modmenu/util/BadgeRenderer.java @@ -2,8 +2,7 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.ModListScreen; +import io.github.prospector.modmenu.gui.ModsScreen; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.client.Minecraft; @@ -17,11 +16,9 @@ public class BadgeRenderer { protected ModContainer container; protected ModMetadata metadata; protected Minecraft client; - protected final ModListScreen screen; + protected final ModsScreen screen; - ModMenuConfig config = ModMenuConfigManager.getConfig(); - - public BadgeRenderer(Minecraft client, int startX, int startY, int endX, ModContainer container, ModListScreen screen) { + public BadgeRenderer(Minecraft client, int startX, int startY, int endX, ModContainer container, ModsScreen screen) { this.startX = startX; this.startY = startY; this.badgeMax = endX; @@ -56,7 +53,7 @@ public void draw(int mouseX, int mouseY) { drawBadge(entry.getKey(), entry.getValue().getKey(), entry.getValue().getValue(), mouseX, mouseY); } } - if (config.getEasterEggs()) { + if (ModMenuConfig.EASTER_EGGS.getValue()) { //noinspection MagicConstant if (Calendar.getInstance().get(0b10) == 0b11 && Calendar.getInstance().get(0b101) == 0x1) { if (metadata.getId().equals(new String(new byte[]{109, 111, 100, 109, 101, 110, 117}))) { @@ -71,7 +68,7 @@ public void draw(int mouseX, int mouseY) { } public void drawBadge(String text, int outlineColor, int fillColor, int mouseX, int mouseY) { - if (!config.getHideBadges()) { + if (!ModMenuConfig.HIDE_BADGES.getValue()) { int width = client.font.stringWidth(text) + 6; if (badgeX + width < badgeMax) { RenderUtils.INSTANCE.drawBadge(screen, badgeX, badgeY, width, text, outlineColor, fillColor, 0xCACACA); diff --git a/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java b/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java index 9914b9102..00b84c8fa 100644 --- a/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/DrawingUtil.java @@ -1,6 +1,6 @@ package io.github.prospector.modmenu.util; -import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.config.ModMenuConfig; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.loader.api.ModContainer; @@ -22,7 +22,7 @@ public static void drawRandomVersionBackground(ModContainer container, int x, in Random random = new Random(seed); int color = 0xFF000000 | Color.HSBtoRGB(random.nextFloat(), random.nextFloat() * 0.1f + 0.7f, 0.9f); - if (!ModMenuConfigManager.getConfig().getRandomJavaColors()) { + if (!ModMenuConfig.RANDOM_JAVA_COLORS.getValue()) { color = 0xFFDD5656; } @@ -48,4 +48,4 @@ public static void drawRandomVersionBackground(ModContainer container, int x, in GLRenderer.popFrame(); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java b/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java index ce8e7ed73..72c01d0ea 100644 --- a/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/HttpUtil.java @@ -47,4 +47,4 @@ private static String getModMenuVersion() { return VersionUtil.removeBuildMetadata(container.get().getMetadata().getVersion().getFriendlyString()); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java index f1dd82845..ab8ec216a 100644 --- a/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java +++ b/src/main/java/io/github/prospector/modmenu/util/ModListSearch.java @@ -2,8 +2,7 @@ import io.github.prospector.modmenu.ModMenu; import io.github.prospector.modmenu.config.ModMenuConfig; -import io.github.prospector.modmenu.config.ModMenuConfigManager; -import io.github.prospector.modmenu.gui.ModListScreen; +import io.github.prospector.modmenu.gui.ModsScreen; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.Person; @@ -20,7 +19,7 @@ public static boolean validSearchQuery(String query) { return query != null && !query.isEmpty(); } - public static List search(ModListScreen screen, String query, List candidates) { + public static List search(ModsScreen screen, String query, List candidates) { if (!validSearchQuery(query)) { return candidates; } @@ -29,23 +28,22 @@ public static List search(ModListScreen screen, String query, List .collect(Collectors.toList()); } - private static boolean passesFilters(ModListScreen screen, ModContainer container, String query) { + private static boolean passesFilters(ModsScreen screen, ModContainer container, String query) { ModMetadata metadata = container.getMetadata(); String id = metadata.getId(); I18n i18n = I18n.getInstance(); - ModMenuConfig config = ModMenuConfigManager.getConfig(); String nameTranslation = "modmenu.nameTranslation." + id; String translatedName = i18n.translateKey(nameTranslation); - String modName = !translatedName.equals(nameTranslation) && config.getTranslateNames() ? translatedName : metadata.getName(); + String modName = !translatedName.equals(nameTranslation) && ModMenuConfig.TRANSLATE_NAMES.getValue() ? translatedName : metadata.getName(); String descriptionTranslation = "modmenu.descriptionTranslation." + id; String translatedDescription = i18n.translateKey(descriptionTranslation); - String modDescription = !translatedDescription.equals(descriptionTranslation) && config.getTranslateDescriptions() ? translatedDescription : metadata.getDescription(); + String modDescription = !translatedDescription.equals(descriptionTranslation) && ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue() ? translatedDescription : metadata.getDescription(); String summaryTranslation = "modmenu.summaryTranslation." + id; String translatedSummary = i18n.translateKey(summaryTranslation); - String modSummary = !translatedSummary.equals(summaryTranslation) && config.getTranslateDescriptions() ? translatedSummary : metadata.getDescription(); + String modSummary = !translatedSummary.equals(summaryTranslation) && ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue() ? translatedSummary : metadata.getDescription(); if (modName.toLowerCase(Locale.ROOT).contains(query) // Search mod name || modDescription.toLowerCase(Locale.ROOT).contains(query) // Search mod description diff --git a/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java b/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java index cbdfccbb1..2c3f50669 100644 --- a/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java +++ b/src/main/java/io/github/prospector/modmenu/util/TestModContainer.java @@ -1,9 +1,6 @@ package io.github.prospector.modmenu.util; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.api.SemanticVersion; -import net.fabricmc.loader.api.Version; +import net.fabricmc.loader.api.*; import net.fabricmc.loader.api.metadata.*; import net.fabricmc.loader.util.version.VersionParsingException; diff --git a/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java b/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java index a7b9c22b5..409670b82 100644 --- a/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java +++ b/src/main/java/io/github/prospector/modmenu/util/TranslationUtil.java @@ -60,4 +60,4 @@ public static String translateNumeric(String key, int[]... args) { public static String translationKeyOf(String type, String id) { return type + "." + ModMenu.MOD_ID + "." + id; } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java b/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java index a9102f906..9ee30c835 100644 --- a/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java +++ b/src/main/java/io/github/prospector/modmenu/util/mod/BTAUpdateInfo.java @@ -38,4 +38,4 @@ public String getDownloadLink() { return "https://downloads.betterthanadventure.net/bta-client/release/v%s/bta.v%s.mmc.zip" .formatted(projectId, versionId); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java b/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java index f12cdc03e..8fa82101b 100644 --- a/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java +++ b/src/main/java/io/github/prospector/modmenu/util/mod/FabricMod.java @@ -2,7 +2,7 @@ import io.github.prospector.modmenu.api.UpdateChecker; import io.github.prospector.modmenu.api.UpdateInfo; -import io.github.prospector.modmenu.config.ModMenuConfigManager; +import io.github.prospector.modmenu.config.ModMenuConfig; import io.github.prospector.modmenu.util.BadgeRenderer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; @@ -51,7 +51,7 @@ public FabricMod(ModContainer container) { @Override public boolean isHidden() { - return ModMenuConfigManager.getConfig().getHiddenMods().contains(this.getId()); + return ModMenuConfig.HIDDEN_MODS.getValue().contains(this.getId()); } @Override @@ -61,7 +61,7 @@ public boolean isHidden() { @Override public boolean allowsUpdateChecks() { - if (ModMenuConfigManager.getConfig().getDisableUpdateChecker().contains(this.getId())) { + if (ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue().contains(this.getId())) { return false; } else { return this.allowsUpdateChecks; @@ -112,4 +112,4 @@ public void setUpdateInfo(@Nullable UpdateInfo updateInfo) { } return null; } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java b/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java index c10698077..884682cf8 100644 --- a/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java +++ b/src/main/java/io/github/prospector/modmenu/util/mod/Mod.java @@ -42,4 +42,4 @@ default boolean isHidden() { default @Nullable String getSha512Hash() throws IOException { return null; } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java b/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java index e1989e2f1..3f45f76c9 100644 --- a/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java +++ b/src/main/java/io/github/prospector/modmenu/util/mod/ModrinthUpdateInfo.java @@ -28,4 +28,4 @@ public boolean isUpdateAvailable() { public String getDownloadLink() { return "https://modrinth.com/project/%s/version/%s".formatted(projectId, versionId); } -} \ No newline at end of file +} diff --git a/src/main/resources/assets/modmenu/lang/en_US.lang b/src/main/resources/assets/modmenu/lang/en_US.lang index aefbbcb9c..4df07407f 100644 --- a/src/main/resources/assets/modmenu/lang/en_US.lang +++ b/src/main/resources/assets/modmenu/lang/en_US.lang @@ -101,9 +101,11 @@ modmenu.options=Mod Menu Options option.modmenu.sorting=Sort option.modmenu.sorting.ascending=A-Z option.modmenu.sorting.descending=Z-A +option.modmenu.sorting.has_update=Has Update option.modmenu.show_libraries=Libraries option.modmenu.show_libraries.true=Shown option.modmenu.show_libraries.false=Hidden +option.modmenu.show_libraries.with_config=With Config option.modmenu.hide_config_buttons=Config Buttons option.modmenu.hide_config_buttons.true=Hidden option.modmenu.hide_config_buttons.false=Shown diff --git a/src/main/resources/assets/modmenu/lang/ru_RU.lang b/src/main/resources/assets/modmenu/lang/ru_RU.lang index 1e334b841..4e89d1bee 100644 --- a/src/main/resources/assets/modmenu/lang/ru_RU.lang +++ b/src/main/resources/assets/modmenu/lang/ru_RU.lang @@ -101,9 +101,11 @@ modmenu.options=Настройки списка модов option.modmenu.sorting=Сортировка option.modmenu.sorting.ascending=A-Z option.modmenu.sorting.descending=Z-A +option.modmenu.sorting.has_update=Обновление option.modmenu.show_libraries=Библиотеки option.modmenu.show_libraries.true=Показаны option.modmenu.show_libraries.false=Скрыты +option.modmenu.show_libraries.with_config=Настройки option.modmenu.hide_config_buttons=Кнопка настроек option.modmenu.hide_config_buttons.true=Скрыта option.modmenu.hide_config_buttons.false=Показана diff --git a/src/main/resources/assets/modmenu/lang/uk_UA.lang b/src/main/resources/assets/modmenu/lang/uk_UA.lang index e5803b9dc..8a619bd4e 100644 --- a/src/main/resources/assets/modmenu/lang/uk_UA.lang +++ b/src/main/resources/assets/modmenu/lang/uk_UA.lang @@ -101,9 +101,11 @@ modmenu.options=Налаштування Mod Menu option.modmenu.sorting=Сортування option.modmenu.sorting.ascending=А-Я option.modmenu.sorting.descending=Я-А +option.modmenu.sorting.has_update=Оновлення option.modmenu.show_libraries=Бібліотеки option.modmenu.show_libraries.true=Показано option.modmenu.show_libraries.false=Приховано +option.modmenu.show_libraries.with_config=Налаштув. option.modmenu.hide_config_buttons=Кнопки налашт. option.modmenu.hide_config_buttons.true=Приховано option.modmenu.hide_config_buttons.false=Показано From c6932ec10859bb47de31ea7a65e64145fcfdccbe Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sat, 17 Oct 2026 15:12:11 +0300 Subject: [PATCH 58/59] bettering --- .../github/prospector/modmenu/gui/ModMenuOptionsScreen.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java index 4203e2ab7..f1b8d37eb 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java @@ -12,6 +12,7 @@ import net.minecraft.client.render.renderer.Shaders; import net.minecraft.client.render.tessellator.TessellatorGeneral; import net.minecraft.core.lang.I18n; +import net.minecraft.core.sound.SoundCategory; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; @@ -86,7 +87,7 @@ public void drawButton(Minecraft mc, int mouseX, int mouseY) { } int lastRow = (options.size() - 1) / columns; - contentHeight = startY + (lastRow + 1) * rowHeight + 6; + contentHeight = (startY - TOP) + lastRow * rowHeight + buttonHeight + 4; int doneY = this.height - 28; this.buttons.add(ButtonUtil.createButton(DONE_ID, this.width / 2 - 75, doneY, 150, 20, i18n.translateKey("gui.options.button.done"))); @@ -139,6 +140,8 @@ public void mouseReleased(int mouseX, int mouseY, int mouseButton) { @Override protected void buttonClicked(ButtonElement button) { + this.mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); + if (button.id == DONE_ID) { this.mc.displayScreen(this.parent); return; From 9ff2529ef07ec402fabf00ffc3eb01d5b67a9010 Mon Sep 17 00:00:00 2001 From: Starman <135715391+StarmanMine142@users.noreply.github.com> Date: Sun, 18 Oct 2026 16:32:23 +0300 Subject: [PATCH 59/59] it's not 59th commit --- .../modmenu/gui/ModMenuOptionsScreen.java | 5 ++-- .../gui/widget/DescriptionListWidget.java | 4 +-- .../modmenu/gui/widget/TextFieldWidget.java | 2 +- .../gui/widget/entries/ModListEntry.java | 30 ++++++++++++------- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java b/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java index f1b8d37eb..11e9c0a82 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java +++ b/src/main/java/io/github/prospector/modmenu/gui/ModMenuOptionsScreen.java @@ -24,7 +24,6 @@ public class ModMenuOptionsScreen extends Screen { private final Screen parent; private final List options = new ArrayList<>(); private final List optionButtons = new ArrayList<>(); - private int nextButtonId = 0; private static final int DONE_ID = 1000; private static final int TOP = 32; private static final int BOTTOM_MARGIN = 40; @@ -44,10 +43,10 @@ public void init() { this.buttons.clear(); this.optionButtons.clear(); - this.nextButtonId = 0; this.scrollPosition = 0.0F; this.scrolling = false; + int nextButtonId = 0; int buttonWidth = 150; int buttonHeight = 20; int gap = 4; @@ -141,7 +140,7 @@ public void mouseReleased(int mouseX, int mouseY, int mouseButton) { @Override protected void buttonClicked(ButtonElement button) { this.mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); - + if (button.id == DONE_ID) { this.mc.displayScreen(this.parent); return; diff --git a/src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java index d4dd57b72..157375a70 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/DescriptionListWidget.java @@ -47,7 +47,7 @@ protected int getScrollbarPosition() { return this.width - 6 + left; } - protected class LinkEntry extends DescriptionEntry { + protected static class LinkEntry extends DescriptionEntry { private final String link; private final int indent; private int lastRenderX; @@ -78,7 +78,7 @@ public void mouseClicked(int mouseX, int mouseY, int button) { } } - protected class BTACreditsEntry extends DescriptionEntry { + protected static class BTACreditsEntry extends DescriptionEntry { private final int indent; private int lastRenderX; private int lastRenderY; diff --git a/src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java b/src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java index 64ad0a3cd..83c737e62 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/TextFieldWidget.java @@ -69,7 +69,7 @@ public class TextFieldWidget extends Screen implements ITextField { private final @Nullable String emptyText; - public TextFieldWidget(int x, int y, int width, int height, String emptyText) { + public TextFieldWidget(int x, int y, int width, int height, @Nullable String emptyText) { this.x = x; this.y = y; this.width = width; diff --git a/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java index 258667d23..b0d46b369 100644 --- a/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java +++ b/src/main/java/io/github/prospector/modmenu/gui/widget/entries/ModListEntry.java @@ -31,10 +31,13 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry { private static final Logger LOGGER = LoggerFactory.getLogger(ModMenu.MOD_ID); + public static final String UNKNOWN_ICON = "/assets/" + ModMenu.MOD_ID + "/unknown_icon.png"; protected final Minecraft client; protected final ModContainer container; @@ -189,19 +192,26 @@ public void render(int index, int y, int x, int rowWidth, int rowHeight, int mou int textY = y + lineHeight + 2; int maxWidth = rowWidth - iconSize - 7; int maxLines = 2; - int currentLine = 0; + List allLines = new ArrayList<>(); String[] paragraphs = description.split("\r?\n"); - for (String paragraph : paragraphs) { - if (currentLine >= maxLines) break; - - for (String line : RenderUtils.INSTANCE.wrapStringToWidthAsList(font, paragraph, maxWidth)) { - if (currentLine >= maxLines) break; + allLines.addAll(RenderUtils.INSTANCE.wrapStringToWidthAsList(font, paragraph, maxWidth)); + } - this.drawStringNoShadow(font, line, textX, textY + (currentLine * lineHeight), 0x808080); - currentLine++; + if (allLines.size() > maxLines) { + allLines = new ArrayList<>(allLines.subList(0, maxLines)); + String last = allLines.get(maxLines - 1); + String ellipsis = "..."; + int ellipsisWidth = font.stringWidth(ellipsis); + while (!last.isEmpty() && font.stringWidth(last) + ellipsisWidth > maxWidth) { + last = last.substring(0, last.length() - 1); } + allLines.set(maxLines - 1, last + ellipsis); + } + + for (int i = 0; i < allLines.size(); i++) { + this.drawStringNoShadow(font, allLines.get(i), textX, textY + (i * lineHeight), 0x808080); } } } @@ -240,7 +250,7 @@ private BufferedImage createIcon() { } else if (metadata.getId().equals("java")) { icon = "assets/" + ModMenu.MOD_ID + "/java_icon.png"; } else { - icon = "assets/" + ModMenu.MOD_ID + "/unknown_icon.png"; + icon = UNKNOWN_ICON; } path = modMenu.findPath(icon).orElse(null); } @@ -295,7 +305,7 @@ public void bindIconTexture() { if (icon != null) { this.iconLocation = this.client.textureManager.loadBufferedTexture(icon).id(); } else { - this.iconLocation = this.client.textureManager.loadTexture("/assets/modmenu/unknown_icon.png").id(); + this.iconLocation = this.client.textureManager.loadTexture(UNKNOWN_ICON).id(); } } this.client.textureManager.bindTexture(this.iconLocation);