From ccaa21417d7a8aa798092e4491549287208379b8 Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Tue, 8 Apr 2025 20:59:29 -0400 Subject: [PATCH 1/8] Update to 25w15a --- gradle.properties | 19 +++-- .../modmenu/gui/ModsScreen.java | 12 +-- .../widget/LegacyTexturedButtonWidget.java | 4 +- .../modmenu/gui/widget/ModListWidget.java | 83 ++++++------------- .../gui/widget/UpdateAvailableBadge.java | 4 +- .../gui/widget/entries/ModListEntry.java | 16 ++-- .../gui/widget/entries/ParentEntry.java | 4 +- .../modmenu/mixin/MixinTitleScreen.java | 2 +- .../terraformersmc/modmenu/util/HttpUtil.java | 2 +- .../modmenu/util/UpdateCheckerUtil.java | 2 +- 10 files changed, 59 insertions(+), 89 deletions(-) diff --git a/gradle.properties b/gradle.properties index f14d043b..653a775d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,15 @@ -org.gradle.jvmargs=-Xmx2G +org.gradle.jvmargs=-Xmx3G +org.gradle.parallel=true maven_group=com.terraformersmc archive_name=modmenu -minecraft_version=1.21.5-rc1 -yarn_mappings=1.21.5-rc1+build.2 -loader_version=0.16.10 -fabric_version=0.119.2+1.21.5 -text_placeholder_api_version=2.6.0+1.21.5 -quilt_loader_version=0.28.1-beta.1 +minecraft_version=25w15a +yarn_mappings=25w15a+build.2 +loader_version=0.16.13 +fabric_version=0.119.10+1.21.6 +text_placeholder_api_version=2.6.2+1.21.5 +quilt_loader_version=0.29.0-beta.3 # Project Metadata project_name=Mod Menu @@ -21,14 +22,14 @@ default_release_type=stable # Modrinth Metadata modrinth_slug=modmenu modrinth_id=mOgUt4GM -modrinth_game_versions=1.21.5-rc1, 1.21.5-rc2, 1.21.5 +modrinth_game_versions= modrinth_mod_loaders=fabric, quilt modrinth_required_dependencies=fabric-api, placeholder-api # CurseForge Metadata curseforge_slug=modmenu curseforge_id=308702 -curseforge_game_versions=1.21.5-Snapshot, 1.21.5, Fabric, Quilt +curseforge_game_versions=Fabric, Quilt curseforge_required_dependencies=fabric-api, text-placeholder-api curseforge_optional_dependencies= diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index 96155d06..aa0d7ccb 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -2,6 +2,8 @@ import com.google.common.base.Joiner; import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.RenderSystem; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; @@ -17,6 +19,7 @@ import com.terraformersmc.modmenu.util.mod.ModBadgeRenderer; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.SharedConstants; +import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.ConfirmLinkScreen; import net.minecraft.client.gui.screen.ConfirmScreen; @@ -25,7 +28,6 @@ import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.render.RenderLayer; import net.minecraft.client.resource.language.I18n; import net.minecraft.client.toast.SystemToast; import net.minecraft.screen.ScreenTexts; @@ -203,7 +205,7 @@ protected void init() { final Mod mod = Objects.requireNonNull(selected).getMod(); boolean isMinecraft = selected.getMod().getId().equals("minecraft"); if (isMinecraft) { - var url = SharedConstants.getGameVersion().isStable() ? Urls.JAVA_FEEDBACK : Urls.SNAPSHOT_FEEDBACK; + var url = SharedConstants.getGameVersion().stable() ? Urls.JAVA_FEEDBACK : Urls.SNAPSHOT_FEEDBACK; ConfirmLinkScreen.open(this, url, true); } else { var url = mod.getWebsite(); @@ -388,9 +390,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) } RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager._enableBlend(); - drawContext.drawTexture(RenderLayer::getGuiTextured, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32); - GlStateManager._disableBlend(); + drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32); int lineSpacing = textRenderer.fontHeight + 1; int imageOffset = 36; Text name = Text.literal(mod.getTranslatedName()); @@ -464,7 +464,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) } } - private Text computeModCountText(boolean includeLibs, boolean onInit) { + private Text computeModCountText(boolean includeLibs, boolean onInit) { int[] rootMods = formatModCount(ModMenu.ROOT_MODS.values() .stream() .filter(mod -> !mod.isHidden() && !mod.getBadges().contains(Mod.Badge.LIBRARY)) diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java index 5d8198a1..31d61e93 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java @@ -1,9 +1,9 @@ package com.terraformersmc.modmenu.gui.widget; +import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.TexturedButtonWidget; -import net.minecraft.client.render.RenderLayer; import net.minecraft.text.Text; import net.minecraft.util.Identifier; @@ -53,7 +53,7 @@ public void renderWidget(DrawContext context, int mouseX, int mouseY, float delt } context.drawTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, this.texture, this.getX(), this.getY(), diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java index 97cd4d0d..0690e9c3 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java @@ -1,12 +1,5 @@ package com.terraformersmc.modmenu.gui.widget; -import com.mojang.blaze3d.buffers.BufferType; -import com.mojang.blaze3d.buffers.BufferUsage; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexFormat; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.gui.ModsScreen; @@ -18,16 +11,12 @@ import com.terraformersmc.modmenu.util.mod.ModSearch; import com.terraformersmc.modmenu.util.mod.fabric.FabricIconHandler; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.*; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.client.render.*; -import net.minecraft.client.util.BufferAllocator; import net.minecraft.text.Text; -import net.minecraft.util.math.ColorHelper; +import net.minecraft.util.Colors; import net.minecraft.util.math.MathHelper; import org.jetbrains.annotations.Nullable; -import org.joml.Matrix4f; import org.lwjgl.glfw.GLFW; import java.util.*; @@ -220,61 +209,35 @@ private void filter(String searchTerm, boolean refresh, boolean search) { @Override protected void renderList(DrawContext drawContext, int mouseX, int mouseY, float delta) { - int entryCount = this.getEntryCount(); + int entryLeft = this.getRowLeft(); + int entryWidth = this.getRowWidth(); + int entryHeight = this.itemHeight - 4; + int entryCount = this.getEntryCount(); for (int index = 0; index < entryCount; ++index) { int entryTop = this.getRowTop(index) + 2; - int entryBottom = this.getRowTop(index) + this.itemHeight; + int entryBottom = this.getRowBottom(index); if (entryBottom >= this.getY() && entryTop <= this.getBottom()) { - int entryHeight = this.itemHeight - 4; ModListEntry entry = this.getEntry(index); - int rowWidth = this.getRowWidth(); - int entryLeft; if (this.isSelectedEntry(index)) { - Matrix4f matrix = drawContext.getMatrices().peek().getPositionMatrix(); - entryLeft = getRowLeft() - 2 + entry.getXOffset(); - int selectionRight = this.getRowLeft() + rowWidth + 2; - float float_2 = this.isFocused() ? 1.0F : 0.5F; - final int topColor = ColorHelper.fromFloats(1.0F, float_2, float_2, float_2); - final int bottomColor = ColorHelper.fromFloats(1.0F, 0.0F, 0.0F, 0.0F); - RenderPipeline pipeline = RenderPipelines.GUI; - try (BufferAllocator alloc = new BufferAllocator(pipeline.getVertexFormat().getVertexSize() * 4)) { - BufferBuilder bufferBuilder = new BufferBuilder(alloc, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - bufferBuilder.vertex(matrix, entryLeft, entryTop + entryHeight + 2, 0.0F).color(topColor); - bufferBuilder.vertex(matrix, selectionRight, entryTop + entryHeight + 2, 0.0F).color(topColor); - bufferBuilder.vertex(matrix, selectionRight, entryTop - 2, 0.0F).color(topColor); - bufferBuilder.vertex(matrix, entryLeft, entryTop - 2, 0.0F).color(topColor); - bufferBuilder.vertex(matrix, entryLeft + 1, entryTop + entryHeight + 1, 0.0F).color(bottomColor); - bufferBuilder.vertex(matrix, selectionRight - 1, entryTop + entryHeight + 1, 0.0F).color(bottomColor); - bufferBuilder.vertex(matrix, selectionRight - 1, entryTop - 1, 0.0F).color(bottomColor); - bufferBuilder.vertex(matrix, entryLeft + 1, entryTop - 1, 0.0F).color(bottomColor); - try (BuiltBuffer builtBuffer = bufferBuilder.endNullable()) { - if (builtBuffer == null) { - alloc.close(); - return; - } - Framebuffer framebuffer = MinecraftClient.getInstance().getFramebuffer(); - RenderSystem.ShapeIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode()); - VertexFormat.IndexType indexType = autoStorageIndexBuffer.getIndexType(); - GpuBuffer indexBuffer = autoStorageIndexBuffer.getIndexBuffer(builtBuffer.getDrawParameters().indexCount()); - GpuBuffer vertexBuffer = RenderSystem.getDevice().createBuffer(() -> "Mod List", BufferType.VERTICES, BufferUsage.DYNAMIC_WRITE, builtBuffer.getBuffer().remaining()); - RenderSystem.getDevice().createCommandEncoder().writeToBuffer(vertexBuffer, builtBuffer.getBuffer(), 0); - try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(framebuffer.getColorAttachment(), OptionalInt.empty(), framebuffer.getDepthAttachment(), OptionalDouble.empty())) { - renderPass.setPipeline(pipeline); - renderPass.setVertexBuffer(0, vertexBuffer); - renderPass.setIndexBuffer(indexBuffer, indexType); - renderPass.drawIndexed(0, builtBuffer.getDrawParameters().indexCount()); - } - } - } + int entryContentLeft = entryLeft + entry.getXOffset() - 2; + int entryContentWidth = entryWidth - entry.getXOffset() + 4; + this.drawSelectionHighlight( + drawContext, + entryContentLeft, + entryTop, + entryContentWidth, + entryHeight, + this.isFocused() ? Colors.WHITE : Colors.GRAY, + Colors.BLACK + ); } - entryLeft = this.getRowLeft(); entry.render( drawContext, index, entryTop, entryLeft, - rowWidth, + entryWidth, entryHeight, mouseX, mouseY, @@ -285,7 +248,15 @@ protected void renderList(DrawContext drawContext, int mouseX, int mouseY, float } } - public void ensureVisible(ModListEntry entry) { + /** + * Version of {@link #drawSelectionHighlight(DrawContext, int, int, int, int, int)} with unconstrained positioning and sizing. + */ + protected void drawSelectionHighlight(DrawContext context, int x, int y, int width, int height, int borderColor, int fillColor) { + context.fill(x, y - 2, x + width, y + height + 2, borderColor); + context.fill(x + 1, y - 1, x + width - 1, y + height + 1, fillColor); + } + + public void ensureVisible(ModListEntry entry) { super.ensureVisible(entry); } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java index b99179d8..24115838 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java @@ -1,8 +1,8 @@ package com.terraformersmc.modmenu.gui.widget; import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.render.RenderLayer; import net.minecraft.util.Identifier; public class UpdateAvailableBadge { @@ -10,6 +10,6 @@ public class UpdateAvailableBadge { public static void renderBadge(DrawContext drawContext, int x, int y) { RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - drawContext.drawGuiTexture(RenderLayer::getGuiTextured, UPDATE_ICON, x, y, 8, 8); + drawContext.drawGuiTexture(RenderPipelines.GUI_TEXTURED, UPDATE_ICON, x, y, 8, 8); } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java index 8a68c7a2..56607b25 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java @@ -1,6 +1,5 @@ package com.terraformersmc.modmenu.gui.widget.entries; -import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; @@ -12,9 +11,9 @@ import com.terraformersmc.modmenu.util.mod.ModBadgeRenderer; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.client.render.RenderLayer; import net.minecraft.client.texture.NativeImageBackedTexture; import net.minecraft.text.StringVisitable; import net.minecraft.text.Text; @@ -23,7 +22,6 @@ import net.minecraft.util.Util; public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry { - public static final Identifier UNKNOWN_ICON = Identifier.ofVanilla("textures/misc/unknown_pack.png"); private static final Identifier MOD_CONFIGURATION_ICON = Identifier.of(ModMenu.MOD_ID, "textures/gui/mod_configuration.png"); private static final Identifier ERROR_ICON = Identifier.ofVanilla("world_list/error"); private static final Identifier ERROR_HIGHLIGHTED_ICON = Identifier.ofVanilla("world_list/error_highlighted"); @@ -69,9 +67,7 @@ public void render( } RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager._enableBlend(); - drawContext.drawTexture(RenderLayer::getGuiTextured, this.getIconTexture(), x, y, 0.0F, 0.0F, iconSize, iconSize, iconSize, iconSize); - GlStateManager._disableBlend(); + drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.getIconTexture(), x, y, 0.0F, 0.0F, iconSize, iconSize, iconSize, iconSize); Text name = Text.literal(mod.getTranslatedName()); StringVisitable trimmedName = name; @@ -137,7 +133,7 @@ public void render( boolean hoveringIcon = mouseX - x < iconSize; if (this.list.getParent().modScreenErrors.containsKey(modId)) { drawContext.drawGuiTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, hoveringIcon ? ERROR_HIGHLIGHTED_ICON : ERROR_ICON, x, y, @@ -150,8 +146,9 @@ public void render( } } else { int v = hoveringIcon ? iconSize : 0; + // TODO: Fix gray background covering logo? drawContext.drawTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, MOD_CONFIGURATION_ICON, x, y, @@ -195,7 +192,8 @@ public Identifier getIconTexture() { if (this.iconLocation == null) { this.iconLocation = Identifier.of(ModMenu.MOD_ID, mod.getId() + "_icon"); NativeImageBackedTexture icon = mod.getIcon(list.getFabricIconHandler(), 64 * this.client.options.getGuiScale().getValue()); - this.client.getTextureManager().registerTexture(this.iconLocation, icon); + icon.setFilter(false, false); + this.client.getTextureManager().registerTexture(this.iconLocation, icon); } return iconLocation; diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java index 643c0591..b030a87c 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java @@ -7,8 +7,8 @@ import com.terraformersmc.modmenu.util.mod.Mod; import com.terraformersmc.modmenu.util.mod.ModSearch; import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.render.RenderLayer; import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.Util; @@ -111,7 +111,7 @@ public void render( int yOffset = hoveringIcon ? iconSize : 0; RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); drawContext.drawTexture( - RenderLayer::getGuiTextured, + RenderPipelines.GUI_TEXTURED, PARENT_MOD_TEXTURE, x, y, diff --git a/src/main/java/com/terraformersmc/modmenu/mixin/MixinTitleScreen.java b/src/main/java/com/terraformersmc/modmenu/mixin/MixinTitleScreen.java index 8de8103d..e1068d40 100644 --- a/src/main/java/com/terraformersmc/modmenu/mixin/MixinTitleScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/mixin/MixinTitleScreen.java @@ -21,7 +21,7 @@ private int adjustRealmsHeight(int height) { } } - @ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Ljava/lang/String;III)I", ordinal = 0)) + @ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Ljava/lang/String;III)V", ordinal = 0)) private String onRender(String string) { if (ModMenuConfig.MODIFY_TITLE_SCREEN.getValue() && ModMenuConfig.MOD_COUNT_LOCATION.getValue().isOnTitleScreen()) { String count = ModMenu.getDisplayedModCount(); diff --git a/src/main/java/com/terraformersmc/modmenu/util/HttpUtil.java b/src/main/java/com/terraformersmc/modmenu/util/HttpUtil.java index 225c30ba..baef8665 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/HttpUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/HttpUtil.java @@ -29,7 +29,7 @@ private static String buildUserAgent() { String loader = ModMenu.RUNNING_QUILT ? "quilt" : "fabric"; var modMenuVersion = getModMenuVersion(); - var minecraftVersion = SharedConstants.getGameVersion().getName(); + var minecraftVersion = SharedConstants.getGameVersion().name(); // -> TerraformersMC/ModMenu/9.1.0 (1.20.3/quilt/development) return "%s/%s (%s/%s%s)".formatted(ModMenu.GITHUB_REF, modMenuVersion, minecraftVersion, loader, env); diff --git a/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java b/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java index 953b07ce..35650dd7 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerUtil.java @@ -226,7 +226,7 @@ private static UpdateChannel getUpdateChannel(String versionType) { } private static @Nullable Map getUpdatedVersions(Collection modHashes) { - String mcVer = SharedConstants.getGameVersion().getName(); + String mcVer = SharedConstants.getGameVersion().name(); List loaders = ModMenu.RUNNING_QUILT ? List.of("fabric", "quilt") : List.of("fabric"); List updateChannels; From 8fe1c8e02c2a1c2ace9ccd80641409b290fdf8f1 Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Tue, 15 Apr 2025 23:08:10 -0400 Subject: [PATCH 2/8] Update to 25w16a --- gradle.properties | 6 ++-- .../modmenu/gui/ModsScreen.java | 3 +- .../gui/widget/DescriptionListWidget.java | 14 ++++---- .../modmenu/gui/widget/ModListWidget.java | 8 ++--- .../gui/widget/UpdateAvailableBadge.java | 4 +-- .../gui/widget/entries/ModListEntry.java | 33 ++++++++++++------- .../gui/widget/entries/ParentEntry.java | 5 ++- .../modmenu/util/DrawingUtil.java | 17 +++++++--- 8 files changed, 51 insertions(+), 39 deletions(-) diff --git a/gradle.properties b/gradle.properties index 653a775d..0f80e775 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,10 +4,10 @@ org.gradle.parallel=true maven_group=com.terraformersmc archive_name=modmenu -minecraft_version=25w15a -yarn_mappings=25w15a+build.2 +minecraft_version=25w16a +yarn_mappings=25w16a+build.3 loader_version=0.16.13 -fabric_version=0.119.10+1.21.6 +fabric_version=0.120.0+1.21.6 text_placeholder_api_version=2.6.2+1.21.5 quilt_loader_version=0.29.0-beta.3 diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index aa0d7ccb..d89161b0 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -389,8 +389,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, RIGHT_PANE_Y, 32, 32); } - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32); + drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32, 0xFFFFFFFF); int lineSpacing = textRenderer.fontHeight + 1; int imageOffset = 36; Text name = Text.literal(mod.getTranslatedName()); diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java index c2e9aa9a..786051eb 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java @@ -1,7 +1,5 @@ package com.terraformersmc.modmenu.gui.widget; -import com.mojang.blaze3d.buffers.BufferType; -import com.mojang.blaze3d.buffers.BufferUsage; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.RenderPass; @@ -13,7 +11,8 @@ import com.terraformersmc.modmenu.util.mod.Mod; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gl.*; +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.Selectable; @@ -23,7 +22,8 @@ import net.minecraft.client.gui.screen.option.CreditsAndAttributionScreen; import net.minecraft.client.gui.widget.ElementListWidget; import net.minecraft.client.gui.widget.EntryListWidget; -import net.minecraft.client.render.*; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.BuiltBuffer; import net.minecraft.client.util.BufferAllocator; import net.minecraft.text.OrderedText; import net.minecraft.text.Text; @@ -303,9 +303,9 @@ public void renderList(DrawContext drawContext, int mouseX, int mouseY, float de Framebuffer framebuffer = MinecraftClient.getInstance().getFramebuffer(); RenderSystem.ShapeIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode()); VertexFormat.IndexType indexType = autoStorageIndexBuffer.getIndexType(); - GpuBuffer vertexBuffer = RenderSystem.getDevice().createBuffer(() -> "Description List", BufferType.VERTICES, BufferUsage.DYNAMIC_WRITE, builtBuffer.getBuffer().remaining()); + GpuBuffer vertexBuffer = RenderSystem.getDevice().createBuffer(() -> "Description List", GpuBuffer.USAGE_COPY_DST, builtBuffer.getBuffer().remaining()); GpuBuffer indexBuffer = autoStorageIndexBuffer.getIndexBuffer(builtBuffer.getDrawParameters().indexCount()); - RenderSystem.getDevice().createCommandEncoder().writeToBuffer(vertexBuffer, builtBuffer.getBuffer(), 0); + RenderSystem.getDevice().createCommandEncoder().writeToBuffer(vertexBuffer.slice(), builtBuffer.getBuffer()); try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(framebuffer.getColorAttachment(), OptionalInt.empty(), framebuffer.getDepthAttachment(), OptionalDouble.empty())) { renderPass.setPipeline(pipeline); renderPass.setVertexBuffer(0, vertexBuffer); @@ -393,7 +393,7 @@ public void render( x += 11; } - drawContext.drawTextWithShadow(textRenderer, text, x + indent, y, 0xAAAAAA); + drawContext.drawTextWithShadow(textRenderer, text, x + indent, y, 0xFFAAAAAA); } @Override diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java index 0690e9c3..40d7e896 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java @@ -69,7 +69,7 @@ public void select(ModListEntry entry) { this.setSelected(entry); if (entry != null) { Mod mod = entry.getMod(); - this.client.getNarratorManager().narrate(Text.translatable("narrator.select", mod.getTranslatedName()).getString()); + this.client.getNarratorManager().narrate(Text.translatable("narrator.select", mod.getTranslatedName())); } } @@ -120,7 +120,7 @@ protected ModListEntry remove(int index) { public void finalizeInit() { reloadFilters(); - if(restoreScrollY != null) { + if (restoreScrollY != null) { setScrollY(restoreScrollY); restoreScrollY = null; } @@ -325,10 +325,6 @@ public int getRowLeft() { return this.getX() + 6; } - public int getTop() { - return this.getY(); - } - public ModsScreen getParent() { return parent; } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java index 24115838..f425b73f 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java @@ -1,6 +1,5 @@ package com.terraformersmc.modmenu.gui.widget; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.util.Identifier; @@ -9,7 +8,6 @@ public class UpdateAvailableBadge { private static final Identifier UPDATE_ICON = Identifier.ofVanilla("icon/trial_available"); public static void renderBadge(DrawContext drawContext, int x, int y) { - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - drawContext.drawGuiTexture(RenderPipelines.GUI_TEXTURED, UPDATE_ICON, x, y, 8, 8); + drawContext.drawGuiTexture(RenderPipelines.GUI_TEXTURED, UPDATE_ICON, x, y, 8, 8, 0xFFFFFFFF); } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java index 56607b25..2db7bd48 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java @@ -1,6 +1,5 @@ package com.terraformersmc.modmenu.gui.widget.entries; -import com.mojang.blaze3d.systems.RenderSystem; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.gui.widget.ModListWidget; @@ -66,8 +65,19 @@ public void render( DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, y, iconSize, iconSize); } - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.getIconTexture(), x, y, 0.0F, 0.0F, iconSize, iconSize, iconSize, iconSize); + drawContext.drawTexture( + RenderPipelines.GUI_TEXTURED, + this.getIconTexture(), + x, + y, + 0.0F, + 0.0F, + iconSize, + iconSize, + iconSize, + iconSize, + 0xFFFFFFFF + ); Text name = Text.literal(mod.getTranslatedName()); StringVisitable trimmedName = name; @@ -78,13 +88,14 @@ public void render( trimmedName = StringVisitable.concat(font.trimToWidth(name, maxNameWidth - font.getWidth(ellipsis)), ellipsis); } - drawContext.drawText(font, + drawContext.method_71046(); + drawContext.drawTextWithShadow(font, Language.getInstance().reorder(trimmedName), x + iconSize + 3, y + 1, - 0xFFFFFF, - true + 0xFFFFFFFF ); + drawContext.method_71050(); var updateBadgeXOffset = 0; if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() @@ -112,9 +123,9 @@ public void render( (y + client.textRenderer.fontHeight + 2), rowWidth - iconSize - 7, 2, - 0x808080 + 0xFF808080 ); - } else { + } else { DrawingUtil.drawWrappedString( drawContext, mod.getPrefixedVersion(), @@ -122,7 +133,7 @@ public void render( (y + client.textRenderer.fontHeight + 2), rowWidth - iconSize - 7, 2, - 0x808080 + 0xFF808080 ); } @@ -146,7 +157,6 @@ public void render( } } else { int v = hoveringIcon ? iconSize : 0; - // TODO: Fix gray background covering logo? drawContext.drawTexture( RenderPipelines.GUI_TEXTURED, MOD_CONFIGURATION_ICON, @@ -157,7 +167,8 @@ public void render( iconSize, iconSize, textureSize, - textureSize + textureSize, + 0xFFFFFFFF ); } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java index b030a87c..393b7c5a 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java @@ -1,6 +1,5 @@ package com.terraformersmc.modmenu.gui.widget.entries; -import com.mojang.blaze3d.systems.RenderSystem; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.gui.widget.ModListWidget; @@ -109,7 +108,6 @@ public void render( drawContext.fill(x, y, x + iconSize, y + iconSize, 0xA0909090); int xOffset = list.getParent().showModChildren.contains(getMod().getId()) ? iconSize : 0; int yOffset = hoveringIcon ? iconSize : 0; - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); drawContext.drawTexture( RenderPipelines.GUI_TEXTURED, PARENT_MOD_TEXTURE, @@ -120,7 +118,8 @@ public void render( iconSize + xOffset, iconSize + yOffset, ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, - ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256 + ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, + 0xFFFFFFFF ); } } diff --git a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java index a69440a8..ddaccfa9 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java @@ -1,6 +1,5 @@ package com.terraformersmc.modmenu.util; -import com.mojang.blaze3d.systems.RenderSystem; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.util.mod.Mod; import net.fabricmc.api.EnvType; @@ -37,7 +36,6 @@ public static void drawRandomVersionBackground( color = 0xFFDD5656; } - RenderSystem.setShaderColor(1f, 1f, 1f, 1f); drawContext.fill(x, y, x + width, y + height, color); } @@ -54,6 +52,7 @@ public static void drawWrappedString( string = string.substring(0, string.length() - 1); } + drawContext.method_71046(); List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); for (int i = 0; i < strings.size(); i++) { if (i >= lines) { @@ -71,8 +70,15 @@ public static void drawWrappedString( x1 += wrapWidth - CLIENT.textRenderer.getWidth(line); } - drawContext.drawText(CLIENT.textRenderer, line, x1, y + i * CLIENT.textRenderer.fontHeight, color, true); + drawContext.drawTextWithShadow( + CLIENT.textRenderer, + line, + x1, + y + i * CLIENT.textRenderer.fontHeight, + color + ); } + drawContext.method_71050(); } public static void drawBadge( @@ -95,12 +101,15 @@ public static void drawBadge( ); drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); - drawContext.drawText(CLIENT.textRenderer, + drawContext.method_71046(); + drawContext.drawText( + CLIENT.textRenderer, text, (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), y + 1, textColor, false ); + drawContext.method_71050(); } } From a8febaa68420fe0ba7409a8dd314f7dc46280aec Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Tue, 13 May 2025 21:49:56 -0400 Subject: [PATCH 3/8] Update to 25w20a (Very Broken) --- gradle.properties | 10 +++++----- .../com/terraformersmc/modmenu/gui/ModsScreen.java | 9 ++++----- .../modmenu/gui/widget/DescriptionListWidget.java | 4 ++-- .../modmenu/gui/widget/entries/ModListEntry.java | 13 +++++++------ .../terraformersmc/modmenu/util/DrawingUtil.java | 10 +++++----- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0f80e775..a99bd8e0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,11 +4,11 @@ org.gradle.parallel=true maven_group=com.terraformersmc archive_name=modmenu -minecraft_version=25w16a -yarn_mappings=25w16a+build.3 -loader_version=0.16.13 -fabric_version=0.120.0+1.21.6 -text_placeholder_api_version=2.6.2+1.21.5 +minecraft_version=25w20a +yarn_mappings=25w20a+build.1 +loader_version=0.16.14 +fabric_version=0.124.0+1.21.6 +text_placeholder_api_version=2.6.3+1.21.5 quilt_loader_version=0.29.0-beta.3 # Project Metadata diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index d89161b0..72b9936f 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -2,9 +2,6 @@ import com.google.common.base.Joiner; import com.mojang.blaze3d.opengl.GlStateManager; -import com.mojang.blaze3d.pipeline.BlendFunction; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.systems.RenderSystem; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.config.ModMenuConfigManager; @@ -25,12 +22,14 @@ import net.minecraft.client.gui.screen.ConfirmScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.tooltip.TooltipState; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.resource.language.I18n; import net.minecraft.client.toast.SystemToast; import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.OrderedText; import net.minecraft.text.StringVisitable; import net.minecraft.text.Text; import net.minecraft.util.Identifier; @@ -412,7 +411,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) if (mouseX > x + imageOffset && mouseY > RIGHT_PANE_Y + 1 && mouseY < RIGHT_PANE_Y + 1 + textRenderer.fontHeight && mouseX < x + imageOffset + textRenderer.getWidth(trimmedName)) { - this.setTooltip(ModMenuScreenTexts.modIdTooltip(mod.getId())); + drawContext.drawTooltip(ModMenuScreenTexts.modIdTooltip(mod.getId()), mouseX, mouseY); } if (this.init || modBadgeRenderer == null || modBadgeRenderer.getMod() != mod) { @@ -461,7 +460,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) ); } } - } + } private Text computeModCountText(boolean includeLibs, boolean onInit) { int[] rootMods = formatModCount(ModMenu.ROOT_MODS.values() diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java index 786051eb..ab3cda28 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java @@ -306,11 +306,11 @@ public void renderList(DrawContext drawContext, int mouseX, int mouseY, float de GpuBuffer vertexBuffer = RenderSystem.getDevice().createBuffer(() -> "Description List", GpuBuffer.USAGE_COPY_DST, builtBuffer.getBuffer().remaining()); GpuBuffer indexBuffer = autoStorageIndexBuffer.getIndexBuffer(builtBuffer.getDrawParameters().indexCount()); RenderSystem.getDevice().createCommandEncoder().writeToBuffer(vertexBuffer.slice(), builtBuffer.getBuffer()); - try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(framebuffer.getColorAttachment(), OptionalInt.empty(), framebuffer.getDepthAttachment(), OptionalDouble.empty())) { + try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Description List", framebuffer.getColorAttachmentView(), OptionalInt.empty(), framebuffer.getDepthAttachmentView(), OptionalDouble.empty())) { renderPass.setPipeline(pipeline); renderPass.setVertexBuffer(0, vertexBuffer); renderPass.setIndexBuffer(indexBuffer, indexType); - renderPass.drawIndexed(0, builtBuffer.getDrawParameters().indexCount()); + renderPass.drawIndexed(0, 0, builtBuffer.getDrawParameters().indexCount(), 1); } } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java index 2db7bd48..659741e9 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java @@ -13,6 +13,7 @@ import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; +import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.texture.NativeImageBackedTexture; import net.minecraft.text.StringVisitable; import net.minecraft.text.Text; @@ -88,18 +89,18 @@ public void render( trimmedName = StringVisitable.concat(font.trimToWidth(name, maxNameWidth - font.getWidth(ellipsis)), ellipsis); } - drawContext.method_71046(); - drawContext.drawTextWithShadow(font, +// drawContext.method_71046(); + drawContext.drawTextWithShadow( + font, Language.getInstance().reorder(trimmedName), x + iconSize + 3, y + 1, 0xFFFFFFFF ); - drawContext.method_71050(); +// drawContext.method_71050(); var updateBadgeXOffset = 0; - if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() - .contains(modId) && (mod.hasUpdate() || mod.getChildHasUpdate())) { + if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue().contains(modId) && (mod.hasUpdate() || mod.getChildHasUpdate())) { UpdateAvailableBadge.renderBadge(drawContext, x + iconSize + 3 + font.getWidth(name) + 2, y); updateBadgeXOffset = 11; } @@ -153,7 +154,7 @@ public void render( ); if (hoveringIcon) { Throwable e = this.list.getParent().modScreenErrors.get(modId); - this.list.getParent().setTooltip(this.client.textRenderer.wrapLines(ModMenuScreenTexts.configureError(modId, e), 175)); +// this.list.getParent().setTooltip(this.client.textRenderer.wrapLines(ModMenuScreenTexts.configureError(modId, e), 175)); } } else { int v = hoveringIcon ? iconSize : 0; diff --git a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java index ddaccfa9..535935d5 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java @@ -52,7 +52,7 @@ public static void drawWrappedString( string = string.substring(0, string.length() - 1); } - drawContext.method_71046(); +// drawContext.method_71046(); List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); for (int i = 0; i < strings.size(); i++) { if (i >= lines) { @@ -78,7 +78,7 @@ public static void drawWrappedString( color ); } - drawContext.method_71050(); +// drawContext.method_71050(); } public static void drawBadge( @@ -101,8 +101,8 @@ public static void drawBadge( ); drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); - drawContext.method_71046(); - drawContext.drawText( +// drawContext.method_71046(); + drawContext.drawText( CLIENT.textRenderer, text, (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), @@ -110,6 +110,6 @@ public static void drawBadge( textColor, false ); - drawContext.method_71050(); +// drawContext.method_71050(); } } From 9ad85e3b8274323f193543a9a895d88dc7f2cbf5 Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Sat, 7 Jun 2025 17:35:08 -0400 Subject: [PATCH 4/8] Update to latest pre-release & fix issues --- build.gradle | 2 +- gradle.properties | 11 +- .../modmenu/gui/ModsScreen.java | 1197 ++++++++--------- .../gui/widget/DescriptionListWidget.java | 774 +++++------ .../gui/widget/entries/ModListEntry.java | 351 +++-- .../gui/widget/entries/ParentEntry.java | 368 ++--- .../modmenu/util/DrawingUtil.java | 148 +- .../terraformersmc/modmenu/util/mod/Mod.java | 24 +- .../modmenu/util/mod/ModBadgeRenderer.java | 92 +- 9 files changed, 1471 insertions(+), 1496 deletions(-) diff --git a/build.gradle b/build.gradle index c55b5ddf..c9ad371f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + id 'fabric-loom' version "${loom_version}" } apply from: 'https://raw.githubusercontent.com/TerraformersMC/GradleScripts/2.7/ferry.gradle' diff --git a/gradle.properties b/gradle.properties index a99bd8e0..8aae7349 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,16 @@ -org.gradle.jvmargs=-Xmx3G +org.gradle.jvmargs=-Xmx4G org.gradle.parallel=true maven_group=com.terraformersmc archive_name=modmenu -minecraft_version=25w20a -yarn_mappings=25w20a+build.1 +minecraft_version=1.21.6-pre3 +yarn_mappings=1.21.6-pre3+build.2 loader_version=0.16.14 -fabric_version=0.124.0+1.21.6 -text_placeholder_api_version=2.6.3+1.21.5 +fabric_version=0.126.0+1.21.6 +text_placeholder_api_version=2.7.0+1.21.6 quilt_loader_version=0.29.0-beta.3 +loom_version=1.10-SNAPSHOT # Project Metadata project_name=Mod Menu diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index 72b9936f..55b406a0 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -22,14 +22,12 @@ import net.minecraft.client.gui.screen.ConfirmScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.gui.tooltip.TooltipState; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.resource.language.I18n; import net.minecraft.client.toast.SystemToast; import net.minecraft.screen.ScreenTexts; -import net.minecraft.text.OrderedText; import net.minecraft.text.StringVisitable; import net.minecraft.text.Text; import net.minecraft.util.Identifier; @@ -48,605 +46,606 @@ import java.util.stream.Collectors; public class ModsScreen extends Screen { - private static final Identifier FILTERS_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/filters_button.png"); - private static final Identifier CONFIGURE_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/configure_button.png"); - - private static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu | ModsScreen"); - private final Screen previousScreen; - private ModListEntry selected; - private ModBadgeRenderer modBadgeRenderer; - private double scrollPercent = 0; - private boolean keepFilterOptionsShown = false; - private boolean init = false; - private boolean filterOptionsShown = false; - private static final int RIGHT_PANE_Y = 48; - private int paneWidth; - private int rightPaneX; - private int searchBoxX; - private int filtersX; - private int filtersWidth; - private int searchRowWidth; - public final Set showModChildren = new HashSet<>(); - - private TextFieldWidget searchBox; - private @Nullable ClickableWidget filtersButton; - private ClickableWidget sortingButton; - private ClickableWidget librariesButton; - private ModListWidget modList; - private @Nullable ClickableWidget configureButton; - private ClickableWidget websiteButton; - private ClickableWidget issuesButton; - private DescriptionListWidget descriptionListWidget; - - public final Map modHasConfigScreen = new HashMap<>(); - public final Map modScreenErrors = new HashMap<>(); - - private static final Text SEND_FEEDBACK_TEXT = Text.translatable("menu.sendFeedback"); - private static final Text REPORT_BUGS_TEXT = Text.translatable("menu.reportBugs"); - - public ModsScreen(Screen previousScreen) { - super(ModMenuScreenTexts.TITLE); - this.previousScreen = previousScreen; - } - - @Override - public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - if (modList.isMouseOver(mouseX, mouseY)) { - return this.modList.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); - } - - if (descriptionListWidget.isMouseOver(mouseX, mouseY)) { - return this.descriptionListWidget.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); - } - - return false; - } - - @Override - protected void init() { - int paneY = ModMenuConfig.CONFIG_MODE.getValue() ? 48 : 48 + 19; - this.paneWidth = this.width / 2 - 8; - this.rightPaneX = this.width - this.paneWidth; - - // Mod list (initialized early for updateFiltersX) - this.modList = new ModListWidget(this.client, - this.paneWidth, - this.height - paneY - 36, - paneY, - ModMenuConfig.COMPACT_LIST.getValue() ? 23 : 36, - this.modList, - this - ); - this.modList.setX(0); - - // Search box - int filtersButtonSize = (ModMenuConfig.CONFIG_MODE.getValue() ? 0 : 22); - int searchWidthMax = this.paneWidth - 32 - filtersButtonSize; - int searchBoxWidth = ModMenuConfig.CONFIG_MODE.getValue() ? Math.min(200, searchWidthMax) : searchWidthMax; - - this.searchBoxX = this.paneWidth / 2 - searchBoxWidth / 2 - filtersButtonSize / 2; - this.searchBox = new TextFieldWidget(this.textRenderer, - this.searchBoxX, - 22, - searchBoxWidth, - 20, - this.searchBox, - ModMenuScreenTexts.SEARCH - ); - this.searchBox.setChangedListener(text -> { - this.modList.filter(text, false); - }); - - // Filters button - Text sortingText = ModMenuConfig.SORTING.getButtonText(); - Text librariesText = ModMenuConfig.SHOW_LIBRARIES.getButtonText(); - - int sortingWidth = textRenderer.getWidth(sortingText) + 28; - int librariesWidth = textRenderer.getWidth(librariesText) + 20; - - this.filtersWidth = librariesWidth + sortingWidth + 2; - this.searchRowWidth = this.searchBoxX + searchBoxWidth + 22; - - this.updateFiltersX(true); - - if (!ModMenuConfig.CONFIG_MODE.getValue()) { - this.filtersButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS, - button -> { - this.setFilterOptionsShown(!this.filterOptionsShown); - } - ) - .position(this.paneWidth / 2 + searchBoxWidth / 2 - 20 / 2 + 2, 22) - .size(20, 20) - .uv(0, 0, 20) - .texture(FILTERS_BUTTON_LOCATION, 32, 64) - .build(); - - this.filtersButton.setTooltip(Tooltip.of(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS)); - } - - // Sorting button - this.sortingButton = ButtonWidget.builder(sortingText, button -> { - ModMenuConfig.SORTING.cycleValue(); - ModMenuConfigManager.save(); - modList.reloadFilters(); - button.setMessage(ModMenuConfig.SORTING.getButtonText()); - }).position(this.filtersX, 45).size(sortingWidth, 20).build(); - - // Show libraries button - this.librariesButton = ButtonWidget.builder(librariesText, button -> { - ModMenuConfig.SHOW_LIBRARIES.toggleValue(); - ModMenuConfigManager.save(); - modList.reloadFilters(); - button.setMessage(ModMenuConfig.SHOW_LIBRARIES.getButtonText()); - }).position(this.filtersX + sortingWidth + 2, 45).size(librariesWidth, 20).build(); - - // Configure button - if (!ModMenuConfig.HIDE_CONFIG_BUTTONS.getValue()) { - this.configureButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ScreenTexts.EMPTY, button -> { - final String id = Objects.requireNonNull(selected).getMod().getId(); - if (getModHasConfigScreen(id)) { - this.safelyOpenConfigScreen(id); - } else { - button.active = false; - } - }) - .position(width - 24, RIGHT_PANE_Y) - .size(20, 20) - .uv(0, 0, 20) - .texture(CONFIGURE_BUTTON_LOCATION, 32, 64) - .build(); - } - - // Website button - int urlButtonWidths = this.paneWidth / 2 - 2; - int cappedButtonWidth = Math.min(urlButtonWidths, 200); - this.websiteButton = ButtonWidget.builder(ModMenuScreenTexts.WEBSITE, button -> { - final Mod mod = Objects.requireNonNull(selected).getMod(); - boolean isMinecraft = selected.getMod().getId().equals("minecraft"); - if (isMinecraft) { - var url = SharedConstants.getGameVersion().stable() ? Urls.JAVA_FEEDBACK : Urls.SNAPSHOT_FEEDBACK; - ConfirmLinkScreen.open(this, url, true); - } else { - var url = mod.getWebsite(); - ConfirmLinkScreen.open(this, url, false); - } - }) - .position(this.rightPaneX + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) - .size(Math.min(urlButtonWidths, 200), 20) - .build(); - - // Issues button - this.issuesButton = ButtonWidget.builder(ModMenuScreenTexts.ISSUES, button -> { - final Mod mod = Objects.requireNonNull(selected).getMod(); - boolean isMinecraft = selected.getMod().getId().equals("minecraft"); - if (isMinecraft) { - ConfirmLinkScreen.open(this, Urls.SNAPSHOT_BUGS, true); - } else { - var url = mod.getIssueTracker(); - ConfirmLinkScreen.open(this, url, false); - } - }) - .position(this.rightPaneX + urlButtonWidths + 4 + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) - .size(Math.min(urlButtonWidths, 200), 20) - .build(); - - // Description list - this.descriptionListWidget = new DescriptionListWidget( - this.client, - this.paneWidth, - this.height - RIGHT_PANE_Y - 96, - RIGHT_PANE_Y + 60, - textRenderer.fontHeight + 1, - this.descriptionListWidget, - this - ); - this.descriptionListWidget.setX(this.rightPaneX); - - // Mods folder button - ClickableWidget modsFolderButton = ButtonWidget.builder(ModMenuScreenTexts.MODS_FOLDER, button -> Util.getOperatingSystem().open(FabricLoader.getInstance().getGameDir().resolve("mods").toUri())).position(this.width / 2 - 154, this.height - 28).size(150, 20).build(); - - // Done button - ClickableWidget doneButton = ButtonWidget.builder(ScreenTexts.DONE, button -> client.setScreen(previousScreen)).position(this.width / 2 + 4, this.height - 28).size(150, 20).build(); - - // Initialize data - modList.finalizeInit(); - this.setFilterOptionsShown(this.keepFilterOptionsShown && this.filterOptionsShown); - - // Add children - this.addSelectableChild(this.searchBox); - this.setInitialFocus(this.searchBox); - if (this.filtersButton != null) { - this.addDrawableChild(this.filtersButton); - } - - this.addDrawableChild(this.sortingButton); - this.addDrawableChild(this.librariesButton); - this.addSelectableChild(this.modList); - if (this.configureButton != null) { - this.addDrawableChild(this.configureButton); - } - - this.addDrawableChild(this.websiteButton); - this.addDrawableChild(this.issuesButton); - this.addSelectableChild(this.descriptionListWidget); - this.addDrawableChild(modsFolderButton); - this.addDrawableChild(doneButton); - - this.init = true; - this.keepFilterOptionsShown = true; - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - return super.keyPressed(keyCode, scanCode, modifiers) || this.searchBox.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean charTyped(char chr, int keyCode) { - return this.searchBox.charTyped(chr, keyCode); - } - - @Override - public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) { - super.render(drawContext, mouseX, mouseY, delta); - ModListEntry selectedEntry = selected; - if (selectedEntry != null) { - this.descriptionListWidget.render(drawContext, mouseX, mouseY, delta); - } - - this.modList.render(drawContext, mouseX, mouseY, delta); - this.searchBox.render(drawContext, mouseX, mouseY, delta); - GlStateManager._disableBlend(); - drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.modList.getWidth() / 2, 8, 16777215); - assert client != null; - int grayColor = 11184810; - if (!ModMenuConfig.DISABLE_DRAG_AND_DROP.getValue()) { - drawContext.drawCenteredTextWithShadow( - this.textRenderer, - ModMenuScreenTexts.DROP_INFO_LINE_1, - this.width - this.modList.getWidth() / 2, - RIGHT_PANE_Y / 2 - client.textRenderer.fontHeight - 1, - grayColor - ); - drawContext.drawCenteredTextWithShadow( - this.textRenderer, - ModMenuScreenTexts.DROP_INFO_LINE_2, - this.width - this.modList.getWidth() / 2, - RIGHT_PANE_Y / 2 + 1, - grayColor - ); - } - - if (!ModMenuConfig.CONFIG_MODE.getValue()) { - Text fullModCount = this.computeModCountText(true, false); - if (!ModMenuConfig.CONFIG_MODE.getValue() && this.updateFiltersX(false)) { - if (this.filterOptionsShown) { - if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || - textRenderer.getWidth(fullModCount) <= this.filtersX - 5) { - drawContext.drawText( - textRenderer, - fullModCount.asOrderedText(), - this.searchBoxX, - 52, - 0xFFFFFF, - true - ); - } else { - drawContext.drawText( - textRenderer, - computeModCountText(false, false).asOrderedText(), - this.searchBoxX, - 46, - 0xFFFFFF, - true - ); - drawContext.drawText( - textRenderer, - computeLibraryCountText(false).asOrderedText(), - this.searchBoxX, - 57, - 0xFFFFFF, - true - ); - } - } else { - if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || - textRenderer.getWidth(fullModCount) <= modList.getWidth() - 5) { - drawContext.drawText(textRenderer, - fullModCount.asOrderedText(), - this.searchBoxX, - 52, - 0xFFFFFF, - true - ); - } else { - drawContext.drawText( - textRenderer, - computeModCountText(false, false).asOrderedText(), - this.searchBoxX, - 46, - 0xFFFFFF, - true - ); - drawContext.drawText( - textRenderer, - computeLibraryCountText(false).asOrderedText(), - this.searchBoxX, - 57, - 0xFFFFFF, - true - ); - } - } - } - } - - if (selectedEntry != null) { - Mod mod = selectedEntry.getMod(); - int x = this.rightPaneX; - if ("java".equals(mod.getId())) { - DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, RIGHT_PANE_Y, 32, 32); - } - - drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32, 0xFFFFFFFF); - int lineSpacing = textRenderer.fontHeight + 1; - int imageOffset = 36; - Text name = Text.literal(mod.getTranslatedName()); - StringVisitable trimmedName = name; - int maxNameWidth = this.width - (x + imageOffset); - if (textRenderer.getWidth(name) > maxNameWidth) { - StringVisitable ellipsis = StringVisitable.plain("..."); - trimmedName = StringVisitable.concat(textRenderer.trimToWidth(name, maxNameWidth - textRenderer.getWidth(ellipsis)), ellipsis); - } - - drawContext.drawText( - textRenderer, - Language.getInstance().reorder(trimmedName), - x + imageOffset, - RIGHT_PANE_Y + 1, - 0xFFFFFF, - true - ); - - if (mouseX > x + imageOffset && mouseY > RIGHT_PANE_Y + 1 && - mouseY < RIGHT_PANE_Y + 1 + textRenderer.fontHeight && - mouseX < x + imageOffset + textRenderer.getWidth(trimmedName)) { + private static final Identifier FILTERS_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/filters_button.png"); + private static final Identifier CONFIGURE_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/configure_button.png"); + + private static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu | ModsScreen"); + private final Screen previousScreen; + private ModListEntry selected; + private ModBadgeRenderer modBadgeRenderer; + private double scrollPercent = 0; + private boolean keepFilterOptionsShown = false; + private boolean init = false; + private boolean filterOptionsShown = false; + private static final int RIGHT_PANE_Y = 48; + private int paneWidth; + private int rightPaneX; + private int searchBoxX; + private int filtersX; + private int filtersWidth; + private int searchRowWidth; + public final Set showModChildren = new HashSet<>(); + + private TextFieldWidget searchBox; + private @Nullable ClickableWidget filtersButton; + private ClickableWidget sortingButton; + private ClickableWidget librariesButton; + private ModListWidget modList; + private @Nullable ClickableWidget configureButton; + private ClickableWidget websiteButton; + private ClickableWidget issuesButton; + private DescriptionListWidget descriptionListWidget; + + public final Map modHasConfigScreen = new HashMap<>(); + public final Map modScreenErrors = new HashMap<>(); + + private static final Text SEND_FEEDBACK_TEXT = Text.translatable("menu.sendFeedback"); + private static final Text REPORT_BUGS_TEXT = Text.translatable("menu.reportBugs"); + + public ModsScreen(Screen previousScreen) { + super(ModMenuScreenTexts.TITLE); + this.previousScreen = previousScreen; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { + if (modList.isMouseOver(mouseX, mouseY)) { + return this.modList.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); + } + + if (descriptionListWidget.isMouseOver(mouseX, mouseY)) { + return this.descriptionListWidget.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); + } + + return false; + } + + @Override + protected void init() { + int paneY = ModMenuConfig.CONFIG_MODE.getValue() ? 48 : 48 + 19; + this.paneWidth = this.width / 2 - 8; + this.rightPaneX = this.width - this.paneWidth; + + // Mod list (initialized early for updateFiltersX) + this.modList = new ModListWidget(this.client, + this.paneWidth, + this.height - paneY - 36, + paneY, + ModMenuConfig.COMPACT_LIST.getValue() ? 23 : 36, + this.modList, + this + ); + this.modList.setX(0); + + // Search box + int filtersButtonSize = (ModMenuConfig.CONFIG_MODE.getValue() ? 0 : 22); + int searchWidthMax = this.paneWidth - 32 - filtersButtonSize; + int searchBoxWidth = ModMenuConfig.CONFIG_MODE.getValue() ? Math.min(200, searchWidthMax) : searchWidthMax; + + this.searchBoxX = this.paneWidth / 2 - searchBoxWidth / 2 - filtersButtonSize / 2; + this.searchBox = new TextFieldWidget(this.textRenderer, + this.searchBoxX, + 22, + searchBoxWidth, + 20, + this.searchBox, + ModMenuScreenTexts.SEARCH + ); + this.searchBox.setChangedListener(text -> { + this.modList.filter(text, false); + }); + + // Filters button + Text sortingText = ModMenuConfig.SORTING.getButtonText(); + Text librariesText = ModMenuConfig.SHOW_LIBRARIES.getButtonText(); + + int sortingWidth = textRenderer.getWidth(sortingText) + 28; + int librariesWidth = textRenderer.getWidth(librariesText) + 20; + + this.filtersWidth = librariesWidth + sortingWidth + 2; + this.searchRowWidth = this.searchBoxX + searchBoxWidth + 22; + + this.updateFiltersX(true); + + if (!ModMenuConfig.CONFIG_MODE.getValue()) { + this.filtersButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS, + button -> { + this.setFilterOptionsShown(!this.filterOptionsShown); + } + ) + .position(this.paneWidth / 2 + searchBoxWidth / 2 - 20 / 2 + 2, 22) + .size(20, 20) + .uv(0, 0, 20) + .texture(FILTERS_BUTTON_LOCATION, 32, 64) + .build(); + + this.filtersButton.setTooltip(Tooltip.of(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS)); + } + + // Sorting button + this.sortingButton = ButtonWidget.builder(sortingText, button -> { + ModMenuConfig.SORTING.cycleValue(); + ModMenuConfigManager.save(); + modList.reloadFilters(); + button.setMessage(ModMenuConfig.SORTING.getButtonText()); + }).position(this.filtersX, 45).size(sortingWidth, 20).build(); + + // Show libraries button + this.librariesButton = ButtonWidget.builder(librariesText, button -> { + ModMenuConfig.SHOW_LIBRARIES.toggleValue(); + ModMenuConfigManager.save(); + modList.reloadFilters(); + button.setMessage(ModMenuConfig.SHOW_LIBRARIES.getButtonText()); + }).position(this.filtersX + sortingWidth + 2, 45).size(librariesWidth, 20).build(); + + // Configure button + if (!ModMenuConfig.HIDE_CONFIG_BUTTONS.getValue()) { + this.configureButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ScreenTexts.EMPTY, button -> { + final String id = Objects.requireNonNull(selected).getMod().getId(); + if (getModHasConfigScreen(id)) { + this.safelyOpenConfigScreen(id); + } else { + button.active = false; + } + }) + .position(width - 24, RIGHT_PANE_Y) + .size(20, 20) + .uv(0, 0, 20) + .texture(CONFIGURE_BUTTON_LOCATION, 32, 64) + .build(); + } + + // Website button + int urlButtonWidths = this.paneWidth / 2 - 2; + int cappedButtonWidth = Math.min(urlButtonWidths, 200); + this.websiteButton = ButtonWidget.builder(ModMenuScreenTexts.WEBSITE, button -> { + final Mod mod = Objects.requireNonNull(selected).getMod(); + boolean isMinecraft = selected.getMod().getId().equals("minecraft"); + if (isMinecraft) { + var url = SharedConstants.getGameVersion().stable() ? Urls.JAVA_FEEDBACK : Urls.SNAPSHOT_FEEDBACK; + ConfirmLinkScreen.open(this, url, true); + } else { + var url = mod.getWebsite(); + ConfirmLinkScreen.open(this, url, false); + } + }) + .position(this.rightPaneX + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) + .size(Math.min(urlButtonWidths, 200), 20) + .build(); + + // Issues button + this.issuesButton = ButtonWidget.builder(ModMenuScreenTexts.ISSUES, button -> { + final Mod mod = Objects.requireNonNull(selected).getMod(); + boolean isMinecraft = selected.getMod().getId().equals("minecraft"); + if (isMinecraft) { + ConfirmLinkScreen.open(this, Urls.SNAPSHOT_BUGS, true); + } else { + var url = mod.getIssueTracker(); + ConfirmLinkScreen.open(this, url, false); + } + }) + .position(this.rightPaneX + urlButtonWidths + 4 + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) + .size(Math.min(urlButtonWidths, 200), 20) + .build(); + + // Description list + this.descriptionListWidget = new DescriptionListWidget( + this.client, + this.paneWidth, + this.height - RIGHT_PANE_Y - 96, + RIGHT_PANE_Y + 60, + textRenderer.fontHeight + 1, + this.descriptionListWidget, + this + ); + this.descriptionListWidget.setX(this.rightPaneX); + + // Mods folder button + ClickableWidget modsFolderButton = ButtonWidget.builder(ModMenuScreenTexts.MODS_FOLDER, button -> Util.getOperatingSystem().open(FabricLoader.getInstance().getGameDir().resolve("mods").toUri())).position(this.width / 2 - 154, this.height - 28).size(150, 20).build(); + + // Done button + ClickableWidget doneButton = ButtonWidget.builder(ScreenTexts.DONE, button -> client.setScreen(previousScreen)).position(this.width / 2 + 4, this.height - 28).size(150, 20).build(); + + // Initialize data + modList.finalizeInit(); + this.setFilterOptionsShown(this.keepFilterOptionsShown && this.filterOptionsShown); + + // Add children + this.addSelectableChild(this.searchBox); + this.setInitialFocus(this.searchBox); + if (this.filtersButton != null) { + this.addDrawableChild(this.filtersButton); + } + + this.addDrawableChild(this.sortingButton); + this.addDrawableChild(this.librariesButton); + this.addSelectableChild(this.modList); + if (this.configureButton != null) { + this.addDrawableChild(this.configureButton); + } + + this.addDrawableChild(this.websiteButton); + this.addDrawableChild(this.issuesButton); + this.addSelectableChild(this.descriptionListWidget); + this.addDrawableChild(modsFolderButton); + this.addDrawableChild(doneButton); + + this.init = true; + this.keepFilterOptionsShown = true; + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return super.keyPressed(keyCode, scanCode, modifiers) || this.searchBox.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean charTyped(char chr, int keyCode) { + return this.searchBox.charTyped(chr, keyCode); + } + + @Override + public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) { + super.render(drawContext, mouseX, mouseY, delta); + ModListEntry selectedEntry = selected; + if (selectedEntry != null) { + this.descriptionListWidget.render(drawContext, mouseX, mouseY, delta); + } + + this.modList.render(drawContext, mouseX, mouseY, delta); + this.searchBox.render(drawContext, mouseX, mouseY, delta); + GlStateManager._disableBlend(); + drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.modList.getWidth() / 2, 8, 16777215); + assert client != null; + int grayColor = 11184810; + if (!ModMenuConfig.DISABLE_DRAG_AND_DROP.getValue()) { + drawContext.drawCenteredTextWithShadow( + this.textRenderer, + ModMenuScreenTexts.DROP_INFO_LINE_1, + this.width - this.modList.getWidth() / 2, + RIGHT_PANE_Y / 2 - client.textRenderer.fontHeight - 1, + grayColor + ); + drawContext.drawCenteredTextWithShadow( + this.textRenderer, + ModMenuScreenTexts.DROP_INFO_LINE_2, + this.width - this.modList.getWidth() / 2, + RIGHT_PANE_Y / 2 + 1, + grayColor + ); + } + + if (!ModMenuConfig.CONFIG_MODE.getValue()) { + Text fullModCount = this.computeModCountText(true, false); + if (!ModMenuConfig.CONFIG_MODE.getValue() && this.updateFiltersX(false)) { + if (this.filterOptionsShown) { + if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || + textRenderer.getWidth(fullModCount) <= this.filtersX - 5) { + drawContext.drawText( + textRenderer, + fullModCount.asOrderedText(), + this.searchBoxX, + 52, + 0xFFFFFFFF, + true + ); + } else { + drawContext.drawText( + textRenderer, + computeModCountText(false, false).asOrderedText(), + this.searchBoxX, + 46, + 0xFFFFFFFF, + true + ); + drawContext.drawText( + textRenderer, + computeLibraryCountText(false).asOrderedText(), + this.searchBoxX, + 57, + 0xFFFFFFFF, + true + ); + } + } else { + if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || + textRenderer.getWidth(fullModCount) <= modList.getWidth() - 5) { + drawContext.drawText( + textRenderer, + fullModCount.asOrderedText(), + this.searchBoxX, + 52, + 0xFFFFFFFF, + true + ); + } else { + drawContext.drawText( + textRenderer, + computeModCountText(false, false).asOrderedText(), + this.searchBoxX, + 46, + 0xFFFFFFFF, + true + ); + drawContext.drawText( + textRenderer, + computeLibraryCountText(false).asOrderedText(), + this.searchBoxX, + 57, + 0xFFFFFFFF, + true + ); + } + } + } + } + + if (selectedEntry != null) { + Mod mod = selectedEntry.getMod(); + int x = this.rightPaneX; + if ("java".equals(mod.getId())) { + DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, RIGHT_PANE_Y, 32, 32); + } + + drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32, 0xFFFFFFFF); + int lineSpacing = textRenderer.fontHeight + 1; + int imageOffset = 36; + Text name = Text.literal(mod.getTranslatedName()); + StringVisitable trimmedName = name; + int maxNameWidth = this.width - (x + imageOffset); + if (textRenderer.getWidth(name) > maxNameWidth) { + StringVisitable ellipsis = StringVisitable.plain("..."); + trimmedName = StringVisitable.concat(textRenderer.trimToWidth(name, maxNameWidth - textRenderer.getWidth(ellipsis)), ellipsis); + } + + drawContext.drawText( + textRenderer, + Language.getInstance().reorder(trimmedName), + x + imageOffset, + RIGHT_PANE_Y + 1, + 0xFFFFFFFF, + true + ); + + if (mouseX > x + imageOffset && mouseY > RIGHT_PANE_Y + 1 && + mouseY < RIGHT_PANE_Y + 1 + textRenderer.fontHeight && + mouseX < x + imageOffset + textRenderer.getWidth(trimmedName)) { drawContext.drawTooltip(ModMenuScreenTexts.modIdTooltip(mod.getId()), mouseX, mouseY); - } - - if (this.init || modBadgeRenderer == null || modBadgeRenderer.getMod() != mod) { - modBadgeRenderer = new ModBadgeRenderer( - x + imageOffset + client.textRenderer.getWidth(trimmedName) + 2, - RIGHT_PANE_Y, - width - 28, - selectedEntry.mod, - this - ); - this.init = false; - } - - if (!ModMenuConfig.HIDE_BADGES.getValue()) { - modBadgeRenderer.draw(drawContext, mouseX, mouseY); - } - - if (mod.isReal()) { - drawContext.drawText( - textRenderer, - mod.getPrefixedVersion(), - x + imageOffset, - RIGHT_PANE_Y + 2 + lineSpacing, - 0x808080, - true - ); - } - - String authors; - List names = mod.getAuthors(); - if (!names.isEmpty()) { - if (names.size() > 1) { - authors = Joiner.on(", ").join(names); - } else { - authors = names.getFirst(); - } - - DrawingUtil.drawWrappedString( - drawContext, - I18n.translate("modmenu.authorPrefix", authors), - x + imageOffset, - RIGHT_PANE_Y + 2 + lineSpacing * 2, - this.paneWidth - imageOffset - 4, - 1, - 0x808080 - ); - } - } + } + + if (this.init || modBadgeRenderer == null || modBadgeRenderer.getMod() != mod) { + modBadgeRenderer = new ModBadgeRenderer( + x + imageOffset + client.textRenderer.getWidth(trimmedName) + 2, + RIGHT_PANE_Y, + width - 28, + selectedEntry.mod, + this + ); + this.init = false; + } + + if (!ModMenuConfig.HIDE_BADGES.getValue()) { + modBadgeRenderer.draw(drawContext, mouseX, mouseY); + } + + if (mod.isReal()) { + drawContext.drawText( + textRenderer, + mod.getPrefixedVersion(), + x + imageOffset, + RIGHT_PANE_Y + 2 + lineSpacing, + 0xFF808080, + true + ); + } + + String authors; + List names = mod.getAuthors(); + if (!names.isEmpty()) { + if (names.size() > 1) { + authors = Joiner.on(", ").join(names); + } else { + authors = names.getFirst(); + } + + DrawingUtil.drawWrappedString( + drawContext, + I18n.translate("modmenu.authorPrefix", authors), + x + imageOffset, + RIGHT_PANE_Y + 2 + lineSpacing * 2, + this.paneWidth - imageOffset - 4, + 1, + 0xFF808080 + ); + } + } } private Text computeModCountText(boolean includeLibs, boolean onInit) { - int[] rootMods = formatModCount(ModMenu.ROOT_MODS.values() - .stream() - .filter(mod -> !mod.isHidden() && !mod.getBadges().contains(Mod.Badge.LIBRARY)) - .map(Mod::getId) - .collect(Collectors.toSet()), onInit); - if (includeLibs && ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { - int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() - .stream() - .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) - .map(Mod::getId) - .collect(Collectors.toSet()), false); - return TranslationUtil.translateNumeric("modmenu.showingModsLibraries", rootMods, rootLibs); - } else { - return TranslationUtil.translateNumeric("modmenu.showingMods", rootMods); - } - } - - private Text computeLibraryCountText(boolean onInit) { - if (ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { - int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() - .stream() - .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) - .map(Mod::getId) - .collect(Collectors.toSet()), false); - return TranslationUtil.translateNumeric("modmenu.showingLibraries", rootLibs); - } else { - return Text.empty(); - } - } - - 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}; - } - } - - @Override - public void close() { - this.modList.close(); - this.client.setScreen(this.previousScreen); - } - - private void setFilterOptionsShown(boolean filterOptionsShown) { - this.filterOptionsShown = filterOptionsShown; - this.sortingButton.visible = filterOptionsShown; - this.librariesButton.visible = filterOptionsShown; - } - - public ModListEntry getSelectedEntry() { - return selected; - } - - public void updateSelectedEntry(ModListEntry entry) { - if (entry == null) { - return; - } - - this.selected = entry; - String modId = selected.getMod().getId(); - - this.descriptionListWidget.updateSelectedModIfRequired(selected.getMod()); - - if (this.configureButton != null) { - - this.configureButton.active = getModHasConfigScreen(modId); - this.configureButton.visible = - selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId); - - if (modScreenErrors.containsKey(modId)) { - Throwable e = modScreenErrors.get(modId); - this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.configureError(modId, e))); - } else { - this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.CONFIGURE)); - } - } - - boolean isMinecraft = modId.equals("minecraft"); - this.websiteButton.setMessage(isMinecraft ? SEND_FEEDBACK_TEXT : ModMenuScreenTexts.WEBSITE); - this.issuesButton.setMessage(isMinecraft ? REPORT_BUGS_TEXT : ModMenuScreenTexts.ISSUES); - - this.websiteButton.visible = true; - this.websiteButton.active = isMinecraft || selected.getMod().getWebsite() != null; - - this.issuesButton.visible = true; - this.issuesButton.active = isMinecraft || selected.getMod().getIssueTracker() != null; - } - - public double getScrollPercent() { - return scrollPercent; - } - - public void updateScrollPercent(double scrollPercent) { - this.scrollPercent = scrollPercent; - } - - public String getSearchInput() { - return this.searchBox.getText(); - } - - private boolean updateFiltersX(boolean onInit) { - if ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(true, onInit)) + 20) >= this.searchRowWidth && - ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(false, onInit)) + 20) >= this.searchRowWidth || - (this.filtersWidth + textRenderer.getWidth(this.computeLibraryCountText(onInit)) + 20) >= this.searchRowWidth - )) { - this.filtersX = this.paneWidth / 2 - this.filtersWidth / 2; - return !filterOptionsShown; - } else { - this.filtersX = this.searchRowWidth - this.filtersWidth + 1; - return true; - } - } - - @Override - public void onFilesDropped(List paths) { - Path modsDirectory = FabricLoader.getInstance().getGameDir().resolve("mods"); - - // Filter out none mods - List mods = paths.stream().filter(ModsScreen::isValidMod).toList(); - if (mods.isEmpty()) { - return; - } - - String modList = mods.stream().map(Path::getFileName).map(Path::toString).collect(Collectors.joining(", ")); - assert this.client != null; - this.client.setScreen(new ConfirmScreen((value) -> { - if (value) { - boolean allSuccessful = true; - for (Path path : mods) { - try { - Files.copy(path, modsDirectory.resolve(path.getFileName())); - } catch (IOException e) { - LOGGER.warn("Failed to copy mod from {} to {}", path, modsDirectory.resolve(path.getFileName())); - SystemToast.addPackCopyFailure(client, path.toString()); - allSuccessful = false; - break; - } - } - - if (allSuccessful) { - SystemToast.add(client.getToastManager(), SystemToast.Type.PERIODIC_NOTIFICATION, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_1, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_2); - } - } - this.client.setScreen(this); - }, ModMenuScreenTexts.DROP_CONFIRM, Text.literal(modList))); - } - - private static boolean isValidMod(Path mod) { - try (JarFile jarFile = new JarFile(mod.toFile())) { - var isFabricMod = jarFile.getEntry("fabric.mod.json") != null; - if (!ModMenu.RUNNING_QUILT) { - return isFabricMod; - } else { - return isFabricMod || jarFile.getEntry("quilt.mod.json") != null; - } - } catch (IOException e) { - return false; - } - } - - public boolean getModHasConfigScreen(String modId) { - if (this.modScreenErrors.containsKey(modId)) { - return false; - } else { - return this.modHasConfigScreen.computeIfAbsent(modId, ModMenu::hasConfigScreen); - } - } - - public void safelyOpenConfigScreen(String modId) { - try { - Screen screen = ModMenu.getConfigScreen(modId, this); - if (screen != null) { - assert this.client != null; - this.client.setScreen(screen); - } - } catch (java.lang.NoClassDefFoundError e) { - LOGGER.warn("The '{}' mod config screen is not available because {} is missing.", modId, e.getLocalizedMessage()); - modScreenErrors.put(modId, e); - } catch (Throwable e) { - LOGGER.error("Error from mod '{}'", modId, e); - modScreenErrors.put(modId, e); - } - } + int[] rootMods = formatModCount(ModMenu.ROOT_MODS.values() + .stream() + .filter(mod -> !mod.isHidden() && !mod.getBadges().contains(Mod.Badge.LIBRARY)) + .map(Mod::getId) + .collect(Collectors.toSet()), onInit); + if (includeLibs && ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { + int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() + .stream() + .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) + .map(Mod::getId) + .collect(Collectors.toSet()), false); + return TranslationUtil.translateNumeric("modmenu.showingModsLibraries", rootMods, rootLibs); + } else { + return TranslationUtil.translateNumeric("modmenu.showingMods", rootMods); + } + } + + private Text computeLibraryCountText(boolean onInit) { + if (ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { + int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() + .stream() + .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) + .map(Mod::getId) + .collect(Collectors.toSet()), false); + return TranslationUtil.translateNumeric("modmenu.showingLibraries", rootLibs); + } else { + return Text.empty(); + } + } + + 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}; + } + } + + @Override + public void close() { + this.modList.close(); + this.client.setScreen(this.previousScreen); + } + + private void setFilterOptionsShown(boolean filterOptionsShown) { + this.filterOptionsShown = filterOptionsShown; + this.sortingButton.visible = filterOptionsShown; + this.librariesButton.visible = filterOptionsShown; + } + + public ModListEntry getSelectedEntry() { + return selected; + } + + public void updateSelectedEntry(ModListEntry entry) { + if (entry == null) { + return; + } + + this.selected = entry; + String modId = selected.getMod().getId(); + + this.descriptionListWidget.updateSelectedModIfRequired(selected.getMod()); + + if (this.configureButton != null) { + + this.configureButton.active = getModHasConfigScreen(modId); + this.configureButton.visible = + selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId); + + if (modScreenErrors.containsKey(modId)) { + Throwable e = modScreenErrors.get(modId); + this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.configureError(modId, e))); + } else { + this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.CONFIGURE)); + } + } + + boolean isMinecraft = modId.equals("minecraft"); + this.websiteButton.setMessage(isMinecraft ? SEND_FEEDBACK_TEXT : ModMenuScreenTexts.WEBSITE); + this.issuesButton.setMessage(isMinecraft ? REPORT_BUGS_TEXT : ModMenuScreenTexts.ISSUES); + + this.websiteButton.visible = true; + this.websiteButton.active = isMinecraft || selected.getMod().getWebsite() != null; + + this.issuesButton.visible = true; + this.issuesButton.active = isMinecraft || selected.getMod().getIssueTracker() != null; + } + + public double getScrollPercent() { + return scrollPercent; + } + + public void updateScrollPercent(double scrollPercent) { + this.scrollPercent = scrollPercent; + } + + public String getSearchInput() { + return this.searchBox.getText(); + } + + private boolean updateFiltersX(boolean onInit) { + if ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(true, onInit)) + 20) >= this.searchRowWidth && + ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(false, onInit)) + 20) >= this.searchRowWidth || + (this.filtersWidth + textRenderer.getWidth(this.computeLibraryCountText(onInit)) + 20) >= this.searchRowWidth + )) { + this.filtersX = this.paneWidth / 2 - this.filtersWidth / 2; + return !filterOptionsShown; + } else { + this.filtersX = this.searchRowWidth - this.filtersWidth + 1; + return true; + } + } + + @Override + public void onFilesDropped(List paths) { + Path modsDirectory = FabricLoader.getInstance().getGameDir().resolve("mods"); + + // Filter out none mods + List mods = paths.stream().filter(ModsScreen::isValidMod).toList(); + if (mods.isEmpty()) { + return; + } + + String modList = mods.stream().map(Path::getFileName).map(Path::toString).collect(Collectors.joining(", ")); + assert this.client != null; + this.client.setScreen(new ConfirmScreen((value) -> { + if (value) { + boolean allSuccessful = true; + for (Path path : mods) { + try { + Files.copy(path, modsDirectory.resolve(path.getFileName())); + } catch (IOException e) { + LOGGER.warn("Failed to copy mod from {} to {}", path, modsDirectory.resolve(path.getFileName())); + SystemToast.addPackCopyFailure(client, path.toString()); + allSuccessful = false; + break; + } + } + + if (allSuccessful) { + SystemToast.add(client.getToastManager(), SystemToast.Type.PERIODIC_NOTIFICATION, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_1, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_2); + } + } + this.client.setScreen(this); + }, ModMenuScreenTexts.DROP_CONFIRM, Text.literal(modList))); + } + + private static boolean isValidMod(Path mod) { + try (JarFile jarFile = new JarFile(mod.toFile())) { + var isFabricMod = jarFile.getEntry("fabric.mod.json") != null; + if (!ModMenu.RUNNING_QUILT) { + return isFabricMod; + } else { + return isFabricMod || jarFile.getEntry("quilt.mod.json") != null; + } + } catch (IOException e) { + return false; + } + } + + public boolean getModHasConfigScreen(String modId) { + if (this.modScreenErrors.containsKey(modId)) { + return false; + } else { + return this.modHasConfigScreen.computeIfAbsent(modId, ModMenu::hasConfigScreen); + } + } + + public void safelyOpenConfigScreen(String modId) { + try { + Screen screen = ModMenu.getConfigScreen(modId, this); + if (screen != null) { + assert this.client != null; + this.client.setScreen(screen); + } + } catch (java.lang.NoClassDefFoundError e) { + LOGGER.warn("The '{}' mod config screen is not available because {} is missing.", modId, e.getLocalizedMessage()); + modScreenErrors.put(modId, e); + } catch (Throwable e) { + LOGGER.error("Error from mod '{}'", modId, e); + modScreenErrors.put(modId, e); + } + } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java index ab3cda28..2bf3ccba 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java @@ -35,257 +35,257 @@ import java.util.*; public class DescriptionListWidget extends EntryListWidget { - private static final Text HAS_UPDATE_TEXT = Text.translatable("modmenu.hasUpdate"); - private static final Text EXPERIMENTAL_TEXT = Text.translatable("modmenu.experimental").formatted(Formatting.GOLD); - private static final Text DOWNLOAD_TEXT = Text.translatable("modmenu.downloadLink").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); - private static final Text CHILD_HAS_UPDATE_TEXT = Text.translatable("modmenu.childHasUpdate"); - private static final Text LINKS_TEXT = Text.translatable("modmenu.links"); - private static final Text SOURCE_TEXT = Text.translatable("modmenu.source").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); - private static final Text LICENSE_TEXT = Text.translatable("modmenu.license"); - private static final Text VIEW_CREDITS_TEXT = Text.translatable("modmenu.viewCredits").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); - private static final Text CREDITS_TEXT = Text.translatable("modmenu.credits"); - - private final ModsScreen parent; - private final TextRenderer textRenderer; - private Mod selectedMod = null; - - public DescriptionListWidget( - MinecraftClient client, - int width, - int height, - int y, - int itemHeight, - DescriptionListWidget copyFrom, - ModsScreen parent - ) { - super(client, width, height, y, itemHeight); - this.parent = parent; - this.textRenderer = client.textRenderer; - - if(copyFrom != null) { - updateSelectedModIfRequired(copyFrom.selectedMod); - setScrollY(copyFrom.getScrollY()); - } - - if(parent.getSelectedEntry() != null) { - updateSelectedModIfRequired(parent.getSelectedEntry().getMod()); - } - } - - @Override - public DescriptionEntry getSelectedOrNull() { - return null; - } - - @Override - public int getRowWidth() { - return this.width - 10; - } - - @Override - protected int getScrollbarX() { - return this.width - 6 + this.getX(); - } - - @Override - public void appendClickableNarrations(NarrationMessageBuilder builder) { - if(selectedMod != null) { - builder.put( - NarrationPart.TITLE, - selectedMod.getTranslatedName() + " " + selectedMod.getPrefixedVersion()); - } - } - - private void rebuildUI() { - if (selectedMod == null) { - return; - } - - DescriptionEntry emptyEntry = new DescriptionEntry(OrderedText.EMPTY); - int wrapWidth = getRowWidth() - 5; - - Mod mod = selectedMod; - Text description = mod.getFormattedDescription(); - if (!description.getString().isEmpty()) { - for (OrderedText line : textRenderer.wrapLines(description, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - } - - if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() - .contains(mod.getId())) { - UpdateInfo updateInfo = mod.getUpdateInfo(); - if (updateInfo != null && updateInfo.isUpdateAvailable()) { - children().add(emptyEntry); - - int index = 0; - for (OrderedText line : textRenderer.wrapLines(HAS_UPDATE_TEXT, wrapWidth - 11)) { - DescriptionEntry entry = new DescriptionEntry(line); - if (index == 0) { - entry.setUpdateTextEntry(); - } - - children().add(entry); - index += 1; - } - - for (OrderedText line : textRenderer.wrapLines(EXPERIMENTAL_TEXT, wrapWidth - 16)) { - children().add(new DescriptionEntry(line, 8)); - } - - - Text updateMessage = updateInfo.getUpdateMessage(); - String downloadLink = updateInfo.getDownloadLink(); - if (updateMessage == null) { - updateMessage = DOWNLOAD_TEXT; - } else { - if (downloadLink != null) { - updateMessage = updateMessage.copy() - .formatted(Formatting.BLUE) - .formatted(Formatting.UNDERLINE); - } - } - for (OrderedText line : textRenderer.wrapLines(updateMessage, wrapWidth - 16)) { - if (downloadLink != null) { - children().add(new LinkEntry(line, downloadLink, 8)); - } else { - children().add(new DescriptionEntry(line, 8)); -} - } - } - - if (mod.getChildHasUpdate()) { - children().add(emptyEntry); - - int index = 0; - for (OrderedText line : textRenderer.wrapLines(CHILD_HAS_UPDATE_TEXT, wrapWidth - 11)) { - DescriptionEntry entry = new DescriptionEntry(line); - if (index == 0) { - entry.setUpdateTextEntry(); - } - - children().add(entry); - index += 1; - } - } - } - - Map links = mod.getLinks(); - String sourceLink = mod.getSource(); - if ((!links.isEmpty() || sourceLink != null) && !ModMenuConfig.HIDE_MOD_LINKS.getValue()) { - children().add(emptyEntry); - - for (OrderedText line : textRenderer.wrapLines(LINKS_TEXT, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - - if (sourceLink != null) { - int indent = 8; - for (OrderedText line : textRenderer.wrapLines(SOURCE_TEXT, wrapWidth - 16)) { - children().add(new LinkEntry(line, sourceLink, indent)); - indent = 16; - } - } - - links.forEach((key, value) -> { - int indent = 8; - for (OrderedText line : textRenderer.wrapLines(Text.translatable(key) - .formatted(Formatting.BLUE) - .formatted(Formatting.UNDERLINE), - wrapWidth - 16 - )) { - children().add(new LinkEntry(line, value, indent)); - indent = 16; - } - }); - } - - Set licenses = mod.getLicense(); - if (!ModMenuConfig.HIDE_MOD_LICENSE.getValue() && !licenses.isEmpty()) { - children().add(emptyEntry); - - for (OrderedText line : textRenderer.wrapLines(LICENSE_TEXT, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - - for (String license : licenses) { - int indent = 8; - for (OrderedText line : textRenderer.wrapLines(Text.literal(license), wrapWidth - 16)) { - children().add(new DescriptionEntry(line, indent)); - indent = 16; - } - } - } - - if (!ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { - if ("minecraft".equals(mod.getId())) { - children().add(emptyEntry); - - for (OrderedText line : textRenderer.wrapLines(VIEW_CREDITS_TEXT, wrapWidth)) { - children().add(new MojangCreditsEntry(line)); - } - } else if (!"java".equals(mod.getId())) { - var credits = mod.getCredits(); - - if (!credits.isEmpty()) { - children().add(emptyEntry); - - for (OrderedText line : textRenderer.wrapLines(CREDITS_TEXT, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - - var iterator = credits.entrySet().iterator(); - - while (iterator.hasNext()) { - int indent = 8; - - var role = iterator.next(); - - - for (var line : textRenderer.wrapLines(this.creditsRoleText(role.getKey()), - wrapWidth - 16 - )) { - children().add(new DescriptionEntry(line, indent)); - indent = 16; - } - - for (var contributor : role.getValue()) { - indent = 16; - - for (var line : textRenderer.wrapLines(Text.literal(contributor), wrapWidth - 24)) { - children().add(new DescriptionEntry(line, indent)); - indent = 24; - } - } - - if (iterator.hasNext()) { - children().add(emptyEntry); - } - } - } - } - } - } - - public void updateSelectedModIfRequired(Mod mod) { - if (mod != selectedMod) { - selectedMod = mod; - clearEntries(); - setScrollY(-Double.MAX_VALUE); - rebuildUI(); - } - } - - @Override - public void renderList(DrawContext drawContext, int mouseX, int mouseY, float delta) { - this.enableScissor(drawContext); - super.renderList(drawContext, mouseX, mouseY, delta); - drawContext.disableScissor(); + private static final Text HAS_UPDATE_TEXT = Text.translatable("modmenu.hasUpdate"); + private static final Text EXPERIMENTAL_TEXT = Text.translatable("modmenu.experimental").formatted(Formatting.GOLD); + private static final Text DOWNLOAD_TEXT = Text.translatable("modmenu.downloadLink").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); + private static final Text CHILD_HAS_UPDATE_TEXT = Text.translatable("modmenu.childHasUpdate"); + private static final Text LINKS_TEXT = Text.translatable("modmenu.links"); + private static final Text SOURCE_TEXT = Text.translatable("modmenu.source").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); + private static final Text LICENSE_TEXT = Text.translatable("modmenu.license"); + private static final Text VIEW_CREDITS_TEXT = Text.translatable("modmenu.viewCredits").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); + private static final Text CREDITS_TEXT = Text.translatable("modmenu.credits"); + + private final ModsScreen parent; + private final TextRenderer textRenderer; + private Mod selectedMod = null; + + public DescriptionListWidget( + MinecraftClient client, + int width, + int height, + int y, + int itemHeight, + DescriptionListWidget copyFrom, + ModsScreen parent + ) { + super(client, width, height, y, itemHeight); + this.parent = parent; + this.textRenderer = client.textRenderer; + + if (copyFrom != null) { + updateSelectedModIfRequired(copyFrom.selectedMod); + setScrollY(copyFrom.getScrollY()); + } + + if (parent.getSelectedEntry() != null) { + updateSelectedModIfRequired(parent.getSelectedEntry().getMod()); + } + } + + @Override + public DescriptionEntry getSelectedOrNull() { + return null; + } + + @Override + public int getRowWidth() { + return this.width - 10; + } + + @Override + protected int getScrollbarX() { + return this.width - 6 + this.getX(); + } + + @Override + public void appendClickableNarrations(NarrationMessageBuilder builder) { + if (selectedMod != null) { + builder.put( + NarrationPart.TITLE, + selectedMod.getTranslatedName() + " " + selectedMod.getPrefixedVersion()); + } + } + + private void rebuildUI() { + if (selectedMod == null) { + return; + } + + DescriptionEntry emptyEntry = new DescriptionEntry(OrderedText.EMPTY); + int wrapWidth = getRowWidth() - 5; + + Mod mod = selectedMod; + Text description = mod.getFormattedDescription(); + if (!description.getString().isEmpty()) { + for (OrderedText line : textRenderer.wrapLines(description, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + } + + if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() + .contains(mod.getId())) { + UpdateInfo updateInfo = mod.getUpdateInfo(); + if (updateInfo != null && updateInfo.isUpdateAvailable()) { + children().add(emptyEntry); + + int index = 0; + for (OrderedText line : textRenderer.wrapLines(HAS_UPDATE_TEXT, wrapWidth - 11)) { + DescriptionEntry entry = new DescriptionEntry(line); + if (index == 0) { + entry.setUpdateTextEntry(); + } + + children().add(entry); + index += 1; + } + + for (OrderedText line : textRenderer.wrapLines(EXPERIMENTAL_TEXT, wrapWidth - 16)) { + children().add(new DescriptionEntry(line, 8)); + } + + + Text updateMessage = updateInfo.getUpdateMessage(); + String downloadLink = updateInfo.getDownloadLink(); + if (updateMessage == null) { + updateMessage = DOWNLOAD_TEXT; + } else { + if (downloadLink != null) { + updateMessage = updateMessage.copy() + .formatted(Formatting.BLUE) + .formatted(Formatting.UNDERLINE); + } + } + for (OrderedText line : textRenderer.wrapLines(updateMessage, wrapWidth - 16)) { + if (downloadLink != null) { + children().add(new LinkEntry(line, downloadLink, 8)); + } else { + children().add(new DescriptionEntry(line, 8)); + } + } + } + + if (mod.getChildHasUpdate()) { + children().add(emptyEntry); + + int index = 0; + for (OrderedText line : textRenderer.wrapLines(CHILD_HAS_UPDATE_TEXT, wrapWidth - 11)) { + DescriptionEntry entry = new DescriptionEntry(line); + if (index == 0) { + entry.setUpdateTextEntry(); + } + + children().add(entry); + index += 1; + } + } + } + + Map links = mod.getLinks(); + String sourceLink = mod.getSource(); + if ((!links.isEmpty() || sourceLink != null) && !ModMenuConfig.HIDE_MOD_LINKS.getValue()) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(LINKS_TEXT, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + + if (sourceLink != null) { + int indent = 8; + for (OrderedText line : textRenderer.wrapLines(SOURCE_TEXT, wrapWidth - 16)) { + children().add(new LinkEntry(line, sourceLink, indent)); + indent = 16; + } + } + + links.forEach((key, value) -> { + int indent = 8; + for (OrderedText line : textRenderer.wrapLines(Text.translatable(key) + .formatted(Formatting.BLUE) + .formatted(Formatting.UNDERLINE), + wrapWidth - 16 + )) { + children().add(new LinkEntry(line, value, indent)); + indent = 16; + } + }); + } + + Set licenses = mod.getLicense(); + if (!ModMenuConfig.HIDE_MOD_LICENSE.getValue() && !licenses.isEmpty()) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(LICENSE_TEXT, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + + for (String license : licenses) { + int indent = 8; + for (OrderedText line : textRenderer.wrapLines(Text.literal(license), wrapWidth - 16)) { + children().add(new DescriptionEntry(line, indent)); + indent = 16; + } + } + } + + if (!ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { + if ("minecraft".equals(mod.getId())) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(VIEW_CREDITS_TEXT, wrapWidth)) { + children().add(new MojangCreditsEntry(line)); + } + } else if (!"java".equals(mod.getId())) { + var credits = mod.getCredits(); + + if (!credits.isEmpty()) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(CREDITS_TEXT, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + + var iterator = credits.entrySet().iterator(); + + while (iterator.hasNext()) { + int indent = 8; + + var role = iterator.next(); + + + for (var line : textRenderer.wrapLines(this.creditsRoleText(role.getKey()), + wrapWidth - 16 + )) { + children().add(new DescriptionEntry(line, indent)); + indent = 16; + } + + for (var contributor : role.getValue()) { + indent = 16; + + for (var line : textRenderer.wrapLines(Text.literal(contributor), wrapWidth - 24)) { + children().add(new DescriptionEntry(line, indent)); + indent = 24; + } + } + + if (iterator.hasNext()) { + children().add(emptyEntry); + } + } + } + } + } + } + + public void updateSelectedModIfRequired(Mod mod) { + if (mod != selectedMod) { + selectedMod = mod; + clearEntries(); + setScrollY(-Double.MAX_VALUE); + rebuildUI(); + } + } + + @Override + public void renderList(DrawContext drawContext, int mouseX, int mouseY, float delta) { + this.enableScissor(drawContext); + super.renderList(drawContext, mouseX, mouseY, delta); + drawContext.disableScissor(); RenderPipeline pipeline = RenderPipelines.GUI; try (BufferAllocator alloc = new BufferAllocator(pipeline.getVertexFormat().getVertexSize() * 4)) { BufferBuilder bufferBuilder = new BufferBuilder(alloc, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - final int black = ColorHelper.fullAlpha(0); + final int black = 0xFF000000; bufferBuilder.vertex(this.getX(), (this.getY() + 4), 0.0F).color(0); bufferBuilder.vertex(this.getRight(), (this.getY() + 4), 0.0F).color(0); bufferBuilder.vertex(this.getRight(), this.getY(), 0.0F).color(black); @@ -314,144 +314,144 @@ public void renderList(DrawContext drawContext, int mouseX, int mouseY, float de } } } - } - - public void renderScrollBar(BufferBuilder bufferBuilder) { - int scrollbarStartX = this.getScrollbarX(); - int scrollbarEndX = scrollbarStartX + 6; - int maxScroll = this.getMaxScrollY(); - if (maxScroll > 0) { - int p = (int) ((float) ((this.getBottom() - this.getY()) * (this.getBottom() - this.getY())) / (float) this.getContentsHeightWithPadding()); - p = MathHelper.clamp(p, 32, this.getBottom() - this.getY() - 8); - int q = (int) this.getScrollY() * (this.getBottom() - this.getY() - p) / maxScroll + this.getY(); - if (q < this.getY()) { - q = this.getY(); - } - - final int black = ColorHelper.fullAlpha(0); - final int firstColor = ColorHelper.fromFloats(255, 128, 128, 128); - final int lastColor = ColorHelper.fromFloats(255, 192, 192, 192); - bufferBuilder.vertex(scrollbarStartX, this.getBottom(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarEndX, this.getBottom(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarEndX, this.getY(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarStartX, this.getY(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarStartX, q + p, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarEndX, q + p, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarEndX, q, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarStartX, q + p - 1, 0.0F).color(lastColor); - bufferBuilder.vertex(scrollbarEndX - 1, q + p - 1, 0.0F).color(lastColor); - bufferBuilder.vertex(scrollbarEndX - 1, q, 0.0F).color(lastColor); - bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(lastColor); - } - } - - private Text creditsRoleText(String roleName) { - // Replace spaces and dashes in role names with underscores if they exist - // Notably Quilted Fabric API does this with FabricMC as "Upstream Owner" - var translationKey = roleName.replaceAll("[ -]", "_").toLowerCase(); - // Add an s to the default untranslated string if it ends in r since this - // Fixes common role names people use in English (e.g. Author -> Authors) - var fallback = roleName.endsWith("r") ? roleName + "s" : roleName; - return Text.translatableWithFallback("modmenu.credits.role." + translationKey, fallback).append(Text.literal(":")); - } - - protected class DescriptionEntry extends ElementListWidget.Entry { - protected OrderedText text; - protected int indent; - public boolean updateTextEntry = false; - - public DescriptionEntry(OrderedText text, int indent) { - this.text = text; - this.indent = indent; - } - - public DescriptionEntry(OrderedText text) { - this(text, 0); - } - - public DescriptionEntry setUpdateTextEntry() { - this.updateTextEntry = true; - return this; - } - - @Override - public void render( - DrawContext drawContext, - int index, - int y, - int x, - int itemWidth, - int itemHeight, - int mouseX, - int mouseY, - boolean isSelected, - float delta - ) { - if (updateTextEntry) { - UpdateAvailableBadge.renderBadge(drawContext, x + indent, y); - x += 11; - } - - drawContext.drawTextWithShadow(textRenderer, text, x + indent, y, 0xFFAAAAAA); - } - - @Override - public List children() { - return Collections.emptyList(); - } - - @Override - public List selectableChildren() { - return Collections.emptyList(); - } - } - - protected class MojangCreditsEntry extends DescriptionEntry { - public MojangCreditsEntry(OrderedText text) { - super(text); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (isMouseOver(mouseX, mouseY)) { - client.setScreen(new MinecraftCredits()); - } - - return super.mouseClicked(mouseX, mouseY, button); - } - - class MinecraftCredits extends CreditsAndAttributionScreen { - public MinecraftCredits() { - super(parent); - } - } - } - - protected class LinkEntry extends DescriptionEntry { - private final String link; - - public LinkEntry(OrderedText text, String link, int indent) { - super(text, indent); - this.link = link; - } - - public LinkEntry(OrderedText text, String link) { - this(text, link, 0); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (isMouseOver(mouseX, mouseY)) { - client.setScreen(new ConfirmLinkScreen((open) -> { - if (open) { - Util.getOperatingSystem().open(link); - } - client.setScreen(parent); - }, link, false)); - } - - return super.mouseClicked(mouseX, mouseY, button); - } - } + } + + public void renderScrollBar(BufferBuilder bufferBuilder) { + int scrollbarStartX = this.getScrollbarX(); + int scrollbarEndX = scrollbarStartX + 6; + int maxScroll = this.getMaxScrollY(); + if (maxScroll > 0) { + int p = (int) ((float) ((this.getBottom() - this.getY()) * (this.getBottom() - this.getY())) / (float) this.getContentsHeightWithPadding()); + p = MathHelper.clamp(p, 32, this.getBottom() - this.getY() - 8); + int q = (int) this.getScrollY() * (this.getBottom() - this.getY() - p) / maxScroll + this.getY(); + if (q < this.getY()) { + q = this.getY(); + } + + final int black = 0xFF000000; + final int firstColor = ColorHelper.fromFloats(255, 128, 128, 128); + final int lastColor = ColorHelper.fromFloats(255, 192, 192, 192); + bufferBuilder.vertex(scrollbarStartX, this.getBottom(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarEndX, this.getBottom(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarEndX, this.getY(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarStartX, this.getY(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarStartX, q + p, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarEndX, q + p, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarEndX, q, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarStartX, q + p - 1, 0.0F).color(lastColor); + bufferBuilder.vertex(scrollbarEndX - 1, q + p - 1, 0.0F).color(lastColor); + bufferBuilder.vertex(scrollbarEndX - 1, q, 0.0F).color(lastColor); + bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(lastColor); + } + } + + private Text creditsRoleText(String roleName) { + // Replace spaces and dashes in role names with underscores if they exist + // Notably Quilted Fabric API does this with FabricMC as "Upstream Owner" + var translationKey = roleName.replaceAll("[ -]", "_").toLowerCase(); + // Add an s to the default untranslated string if it ends in r since this + // Fixes common role names people use in English (e.g. Author -> Authors) + var fallback = roleName.endsWith("r") ? roleName + "s" : roleName; + return Text.translatableWithFallback("modmenu.credits.role." + translationKey, fallback).append(Text.literal(":")); + } + + protected class DescriptionEntry extends ElementListWidget.Entry { + protected OrderedText text; + protected int indent; + public boolean updateTextEntry = false; + + public DescriptionEntry(OrderedText text, int indent) { + this.text = text; + this.indent = indent; + } + + public DescriptionEntry(OrderedText text) { + this(text, 0); + } + + public DescriptionEntry setUpdateTextEntry() { + this.updateTextEntry = true; + return this; + } + + @Override + public void render( + DrawContext drawContext, + int index, + int y, + int x, + int itemWidth, + int itemHeight, + int mouseX, + int mouseY, + boolean isSelected, + float delta + ) { + if (updateTextEntry) { + UpdateAvailableBadge.renderBadge(drawContext, x + indent, y); + x += 11; + } + + drawContext.drawTextWithShadow(textRenderer, text, x + indent, y, 0xFFAAAAAA); + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public List selectableChildren() { + return Collections.emptyList(); + } + } + + protected class MojangCreditsEntry extends DescriptionEntry { + public MojangCreditsEntry(OrderedText text) { + super(text); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isMouseOver(mouseX, mouseY)) { + client.setScreen(new MinecraftCredits()); + } + + return super.mouseClicked(mouseX, mouseY, button); + } + + class MinecraftCredits extends CreditsAndAttributionScreen { + public MinecraftCredits() { + super(parent); + } + } + } + + protected class LinkEntry extends DescriptionEntry { + private final String link; + + public LinkEntry(OrderedText text, String link, int indent) { + super(text, indent); + this.link = link; + } + + public LinkEntry(OrderedText text, String link) { + this(text, link, 0); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isMouseOver(mouseX, mouseY)) { + client.setScreen(new ConfirmLinkScreen((open) -> { + if (open) { + Util.getOperatingSystem().open(link); + } + client.setScreen(parent); + }, link, false)); + } + + return super.mouseClicked(mouseX, mouseY, button); + } + } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java index 659741e9..73c5efbb 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java @@ -5,7 +5,6 @@ import com.terraformersmc.modmenu.gui.widget.ModListWidget; import com.terraformersmc.modmenu.gui.widget.UpdateAvailableBadge; import com.terraformersmc.modmenu.util.DrawingUtil; -import com.terraformersmc.modmenu.util.ModMenuScreenTexts; import com.terraformersmc.modmenu.util.mod.Mod; import com.terraformersmc.modmenu.util.mod.ModBadgeRenderer; import net.minecraft.client.MinecraftClient; @@ -13,60 +12,60 @@ import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.texture.NativeImageBackedTexture; import net.minecraft.text.StringVisitable; import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.Language; import net.minecraft.util.Util; +import net.minecraft.util.math.ColorHelper; public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry { - private static final Identifier MOD_CONFIGURATION_ICON = Identifier.of(ModMenu.MOD_ID, "textures/gui/mod_configuration.png"); - private static final Identifier ERROR_ICON = Identifier.ofVanilla("world_list/error"); - private static final Identifier ERROR_HIGHLIGHTED_ICON = Identifier.ofVanilla("world_list/error_highlighted"); - - protected final MinecraftClient client; - public final Mod mod; - protected final ModListWidget list; - protected Identifier iconLocation; - protected static final int FULL_ICON_SIZE = 32; - protected static final int COMPACT_ICON_SIZE = 19; - protected long sinceLastClick; - - public ModListEntry(Mod mod, ModListWidget list) { - this.mod = mod; - this.list = list; - this.client = MinecraftClient.getInstance(); - } - - @Override - public Text getNarration() { - return Text.literal(mod.getTranslatedName()); - } - - @Override - public void render( - DrawContext drawContext, - int index, - int y, - int x, - int rowWidth, - int rowHeight, - int mouseX, - int mouseY, - boolean hovered, - float delta - ) { - x += getXOffset(); - rowWidth -= getXOffset(); - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - String modId = mod.getId(); - if ("java".equals(modId)) { - DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, y, iconSize, iconSize); - } - - drawContext.drawTexture( + private static final Identifier MOD_CONFIGURATION_ICON = Identifier.of(ModMenu.MOD_ID, "textures/gui/mod_configuration.png"); + private static final Identifier ERROR_ICON = Identifier.ofVanilla("world_list/error"); + private static final Identifier ERROR_HIGHLIGHTED_ICON = Identifier.ofVanilla("world_list/error_highlighted"); + + protected final MinecraftClient client; + public final Mod mod; + protected final ModListWidget list; + protected Identifier iconLocation; + protected static final int FULL_ICON_SIZE = 32; + protected static final int COMPACT_ICON_SIZE = 19; + protected long sinceLastClick; + + public ModListEntry(Mod mod, ModListWidget list) { + this.mod = mod; + this.list = list; + this.client = MinecraftClient.getInstance(); + } + + @Override + public Text getNarration() { + return Text.literal(mod.getTranslatedName()); + } + + @Override + public void render( + DrawContext drawContext, + int index, + int y, + int x, + int rowWidth, + int rowHeight, + int mouseX, + int mouseY, + boolean hovered, + float delta + ) { + x += getXOffset(); + rowWidth -= getXOffset(); + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + String modId = mod.getId(); + if ("java".equals(modId)) { + DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, y, iconSize, iconSize); + } + + drawContext.drawTexture( RenderPipelines.GUI_TEXTURED, this.getIconTexture(), x, @@ -77,141 +76,139 @@ public void render( iconSize, iconSize, iconSize, + ColorHelper.getWhite(1.0F) + ); + + Text name = Text.literal(mod.getTranslatedName()); + StringVisitable trimmedName = name; + int maxNameWidth = rowWidth - iconSize - 3; + TextRenderer font = this.client.textRenderer; + if (font.getWidth(name) > maxNameWidth) { + StringVisitable ellipsis = StringVisitable.plain("..."); + trimmedName = StringVisitable.concat(font.trimToWidth(name, maxNameWidth - font.getWidth(ellipsis)), ellipsis); + } + + drawContext.drawTextWithShadow( + font, + Language.getInstance().reorder(trimmedName), + x + iconSize + 3, + y + 1, 0xFFFFFFFF ); - Text name = Text.literal(mod.getTranslatedName()); - StringVisitable trimmedName = name; - int maxNameWidth = rowWidth - iconSize - 3; - TextRenderer font = this.client.textRenderer; - if (font.getWidth(name) > maxNameWidth) { - StringVisitable ellipsis = StringVisitable.plain("..."); - trimmedName = StringVisitable.concat(font.trimToWidth(name, maxNameWidth - font.getWidth(ellipsis)), ellipsis); - } - -// drawContext.method_71046(); - drawContext.drawTextWithShadow( - font, - Language.getInstance().reorder(trimmedName), - x + iconSize + 3, - y + 1, - 0xFFFFFFFF - ); -// drawContext.method_71050(); - - var updateBadgeXOffset = 0; - if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue().contains(modId) && (mod.hasUpdate() || mod.getChildHasUpdate())) { - UpdateAvailableBadge.renderBadge(drawContext, x + iconSize + 3 + font.getWidth(name) + 2, y); - updateBadgeXOffset = 11; - } - - if (!ModMenuConfig.HIDE_BADGES.getValue()) { - new ModBadgeRenderer( - x + iconSize + 3 + font.getWidth(name) + 2 + updateBadgeXOffset, - y, - x + rowWidth, - mod, - list.getParent() - ).draw(drawContext, mouseX, mouseY); - } - - if (!ModMenuConfig.COMPACT_LIST.getValue()) { - String summary = mod.getSummary(); - DrawingUtil.drawWrappedString( - drawContext, - summary, - (x + iconSize + 3 + 4), - (y + client.textRenderer.fontHeight + 2), - rowWidth - iconSize - 7, - 2, - 0xFF808080 - ); + var updateBadgeXOffset = 0; + if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue().contains(modId) && (mod.hasUpdate() || mod.getChildHasUpdate())) { + UpdateAvailableBadge.renderBadge(drawContext, x + iconSize + 3 + font.getWidth(name) + 2, y); + updateBadgeXOffset = 11; + } + + if (!ModMenuConfig.HIDE_BADGES.getValue()) { + new ModBadgeRenderer( + x + iconSize + 3 + font.getWidth(name) + 2 + updateBadgeXOffset, + y, + x + rowWidth, + mod, + list.getParent() + ).draw(drawContext, mouseX, mouseY); + } + + if (!ModMenuConfig.COMPACT_LIST.getValue()) { + String summary = mod.getSummary(); + DrawingUtil.drawWrappedString( + drawContext, + summary, + (x + iconSize + 3 + 4), + (y + client.textRenderer.fontHeight + 2), + rowWidth - iconSize - 7, + 2, + 0xFF808080 + ); } else { - DrawingUtil.drawWrappedString( - drawContext, - mod.getPrefixedVersion(), - (x + iconSize + 3), - (y + client.textRenderer.fontHeight + 2), - rowWidth - iconSize - 7, - 2, - 0xFF808080 - ); - } - - if (!(this instanceof ParentEntry) && ModMenuConfig.QUICK_CONFIGURE.getValue() && (this.list.getParent().getModHasConfigScreen(modId) || this.list.getParent().modScreenErrors.containsKey(modId))) { - final int textureSize = ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; - if (this.client.options.getTouchscreen().getValue() || hovered) { - drawContext.fill(x, y, x + iconSize, y + iconSize, -1601138544); - boolean hoveringIcon = mouseX - x < iconSize; - if (this.list.getParent().modScreenErrors.containsKey(modId)) { - drawContext.drawGuiTexture( - RenderPipelines.GUI_TEXTURED, - hoveringIcon ? ERROR_HIGHLIGHTED_ICON : ERROR_ICON, - x, - y, - iconSize, - iconSize - ); - if (hoveringIcon) { - Throwable e = this.list.getParent().modScreenErrors.get(modId); -// this.list.getParent().setTooltip(this.client.textRenderer.wrapLines(ModMenuScreenTexts.configureError(modId, e), 175)); - } - } else { - int v = hoveringIcon ? iconSize : 0; - drawContext.drawTexture( - RenderPipelines.GUI_TEXTURED, - MOD_CONFIGURATION_ICON, - x, - y, - 0.0F, - (float) v, - iconSize, - iconSize, - textureSize, - textureSize, - 0xFFFFFFFF - ); - } - } - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int delta) { - list.select(this); - if (ModMenuConfig.QUICK_CONFIGURE.getValue() && this.list.getParent().getModHasConfigScreen(this.mod.getId())) { - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - if (mouseX - list.getRowLeft() <= iconSize) { - this.openConfig(); - } else if (Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { - this.openConfig(); - } - } - - this.sinceLastClick = Util.getMeasuringTimeMs(); - return true; - } - - public void openConfig() { - this.list.getParent().safelyOpenConfigScreen(mod.getId()); - } - - public Mod getMod() { - return mod; - } - - public Identifier getIconTexture() { - if (this.iconLocation == null) { - this.iconLocation = Identifier.of(ModMenu.MOD_ID, mod.getId() + "_icon"); - NativeImageBackedTexture icon = mod.getIcon(list.getFabricIconHandler(), 64 * this.client.options.getGuiScale().getValue()); - icon.setFilter(false, false); + DrawingUtil.drawWrappedString( + drawContext, + mod.getPrefixedVersion(), + (x + iconSize + 3), + (y + client.textRenderer.fontHeight + 2), + rowWidth - iconSize - 7, + 2, + 0xFF808080 + ); + } + + if (!(this instanceof ParentEntry) && ModMenuConfig.QUICK_CONFIGURE.getValue() && (this.list.getParent().getModHasConfigScreen(modId) || this.list.getParent().modScreenErrors.containsKey(modId))) { + final int textureSize = ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; + if (this.client.options.getTouchscreen().getValue() || hovered) { + drawContext.fill(x, y, x + iconSize, y + iconSize, -1601138544); + boolean hoveringIcon = mouseX - x < iconSize; + if (this.list.getParent().modScreenErrors.containsKey(modId)) { + drawContext.drawGuiTexture( + RenderPipelines.GUI_TEXTURED, + hoveringIcon ? ERROR_HIGHLIGHTED_ICON : ERROR_ICON, + x, + y, + iconSize, + iconSize + ); + if (hoveringIcon) { + Throwable e = this.list.getParent().modScreenErrors.get(modId); + // this.list.getParent().setTooltip(this.client.textRenderer.wrapLines(ModMenuScreenTexts.configureError(modId, e), 175)); + } + } else { + int v = hoveringIcon ? iconSize : 0; + drawContext.drawTexture( + RenderPipelines.GUI_TEXTURED, + MOD_CONFIGURATION_ICON, + x, + y, + 0.0F, + (float) v, + iconSize, + iconSize, + textureSize, + textureSize, + ColorHelper.getWhite(1.0F) + ); + } + } + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int delta) { + list.select(this); + if (ModMenuConfig.QUICK_CONFIGURE.getValue() && this.list.getParent().getModHasConfigScreen(this.mod.getId())) { + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + if (mouseX - list.getRowLeft() <= iconSize) { + this.openConfig(); + } else if (Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { + this.openConfig(); + } + } + + this.sinceLastClick = Util.getMeasuringTimeMs(); + return true; + } + + public void openConfig() { + this.list.getParent().safelyOpenConfigScreen(mod.getId()); + } + + public Mod getMod() { + return mod; + } + + public Identifier getIconTexture() { + if (this.iconLocation == null) { + this.iconLocation = Identifier.of(ModMenu.MOD_ID, mod.getId() + "_icon"); + NativeImageBackedTexture icon = mod.getIcon(list.getFabricIconHandler(), 64 * this.client.options.getGuiScale().getValue()); + icon.setFilter(false, false); this.client.getTextureManager().registerTexture(this.iconLocation, icon); - } + } - return iconLocation; - } + return iconLocation; + } - public int getXOffset() { - return 0; - } + public int getXOffset() { + return 0; + } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java index 393b7c5a..b9151c8a 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java @@ -18,188 +18,188 @@ import java.util.Objects; public class ParentEntry extends ModListEntry { - private static final Identifier PARENT_MOD_TEXTURE = Identifier.of(ModMenu.MOD_ID, "textures/gui/parent_mod.png"); - protected List children; - protected ModListWidget list; - protected boolean hoveringIcon = false; - - public ParentEntry(Mod parent, List children, ModListWidget list) { - super(parent, list); - this.children = children; - this.list = list; - } - - @Override - public void render( - DrawContext drawContext, - int index, - int y, - int x, - int rowWidth, - int rowHeight, - int mouseX, - int mouseY, - boolean isSelected, - float delta - ) { - super.render(drawContext, index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); - TextRenderer font = client.textRenderer; - int childrenBadgeHeight = font.fontHeight; - int childrenBadgeWidth = font.fontHeight; - int shownChildren = ModSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); - Text str = shownChildren == children.size() ? - Text.literal(String.valueOf(shownChildren)) : - Text.literal(shownChildren + "/" + children.size()); - int childrenWidth = font.getWidth(str) - 1; - if (childrenBadgeWidth < childrenWidth + 4) { - childrenBadgeWidth = childrenWidth + 4; - } - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - int childrenBadgeX = x + iconSize - childrenBadgeWidth; - int childrenBadgeY = y + iconSize - childrenBadgeHeight; - int childrenOutlineColor = 0xff107454; - int childrenFillColor = 0xff093929; - drawContext.fill( - childrenBadgeX + 1, - childrenBadgeY, - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + 1, - childrenOutlineColor - ); - drawContext.fill( - childrenBadgeX, - childrenBadgeY + 1, - childrenBadgeX + 1, - childrenBadgeY + childrenBadgeHeight - 1, - childrenOutlineColor - ); - drawContext.fill( - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + 1, - childrenBadgeX + childrenBadgeWidth, - childrenBadgeY + childrenBadgeHeight - 1, - childrenOutlineColor - ); - drawContext.fill( - childrenBadgeX + 1, - childrenBadgeY + 1, - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + childrenBadgeHeight - 1, - childrenFillColor - ); - drawContext.fill( - childrenBadgeX + 1, - childrenBadgeY + childrenBadgeHeight - 1, - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + childrenBadgeHeight, - childrenOutlineColor - ); - drawContext.drawText( - font, - str.asOrderedText(), - (int) (childrenBadgeX + (float) childrenBadgeWidth / 2 - (float) childrenWidth / 2), - childrenBadgeY + 1, - 0xCACACA, - false - ); - - this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; - if (isMouseOver(mouseX, mouseY)) { - drawContext.fill(x, y, x + iconSize, y + iconSize, 0xA0909090); - int xOffset = list.getParent().showModChildren.contains(getMod().getId()) ? iconSize : 0; - int yOffset = hoveringIcon ? iconSize : 0; - drawContext.drawTexture( - RenderPipelines.GUI_TEXTURED, - PARENT_MOD_TEXTURE, - x, - y, - xOffset, - yOffset, - iconSize + xOffset, - iconSize + yOffset, - ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, - ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, - 0xFFFFFFFF - ); - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int i) { - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - boolean quickConfigure = ModMenuConfig.QUICK_CONFIGURE.getValue(); - if (mouseX - list.getRowLeft() <= iconSize) { - this.toggleChildren(); - return true; - } else if (!quickConfigure && Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { - this.toggleChildren(); - return true; - } else { - return super.mouseClicked(mouseX, mouseY, i); - } - } - - private void toggleChildren() { - String id = getMod().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 keyCode, int scanCode, int modifiers) { - String modId = getMod().getId(); - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE) { - if (list.getParent().showModChildren.contains(modId)) { - list.getParent().showModChildren.remove(modId); - } else { - list.getParent().showModChildren.add(modId); - } - - list.filter(list.getParent().getSearchInput(), false); - return true; - } else if (keyCode == GLFW.GLFW_KEY_LEFT) { - if (list.getParent().showModChildren.contains(modId)) { - list.getParent().showModChildren.remove(modId); - list.filter(list.getParent().getSearchInput(), false); - } - - return true; - } else if (keyCode == GLFW.GLFW_KEY_RIGHT) { - if (!list.getParent().showModChildren.contains(modId)) { - list.getParent().showModChildren.add(modId); - list.filter(list.getParent().getSearchInput(), false); - return true; - } else { - return list.keyPressed(GLFW.GLFW_KEY_DOWN, 0, 0); - } - } - - return super.keyPressed(keyCode, scanCode, modifiers); - } - - public void setChildren(List children) { - this.children = children; - } - - public void addChildren(List children) { - this.children.addAll(children); - } - - public void addChildren(Mod... children) { - this.children.addAll(Arrays.asList(children)); - } - - public List getChildren() { - return children; - } - - @Override - public boolean isMouseOver(double double_1, double double_2) { - return Objects.equals(this.list.getEntryAtPos(double_1, double_2), this); - } + private static final Identifier PARENT_MOD_TEXTURE = Identifier.of(ModMenu.MOD_ID, "textures/gui/parent_mod.png"); + protected List children; + protected ModListWidget list; + protected boolean hoveringIcon = false; + + public ParentEntry(Mod parent, List children, ModListWidget list) { + super(parent, list); + this.children = children; + this.list = list; + } + + @Override + public void render( + DrawContext drawContext, + int index, + int y, + int x, + int rowWidth, + int rowHeight, + int mouseX, + int mouseY, + boolean isSelected, + float delta + ) { + super.render(drawContext, index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); + TextRenderer font = client.textRenderer; + int childrenBadgeHeight = font.fontHeight; + int childrenBadgeWidth = font.fontHeight; + int shownChildren = ModSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); + Text str = shownChildren == children.size() ? + Text.literal(String.valueOf(shownChildren)) : + Text.literal(shownChildren + "/" + children.size()); + int childrenWidth = font.getWidth(str) - 1; + if (childrenBadgeWidth < childrenWidth + 4) { + childrenBadgeWidth = childrenWidth + 4; + } + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + int childrenBadgeX = x + iconSize - childrenBadgeWidth; + int childrenBadgeY = y + iconSize - childrenBadgeHeight; + int childrenOutlineColor = 0xFF107454; + int childrenFillColor = 0xFF093929; + drawContext.fill( + childrenBadgeX + 1, + childrenBadgeY, + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + 1, + childrenOutlineColor + ); + drawContext.fill( + childrenBadgeX, + childrenBadgeY + 1, + childrenBadgeX + 1, + childrenBadgeY + childrenBadgeHeight - 1, + childrenOutlineColor + ); + drawContext.fill( + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + 1, + childrenBadgeX + childrenBadgeWidth, + childrenBadgeY + childrenBadgeHeight - 1, + childrenOutlineColor + ); + drawContext.fill( + childrenBadgeX + 1, + childrenBadgeY + 1, + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + childrenBadgeHeight - 1, + childrenFillColor + ); + drawContext.fill( + childrenBadgeX + 1, + childrenBadgeY + childrenBadgeHeight - 1, + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + childrenBadgeHeight, + childrenOutlineColor + ); + drawContext.drawText( + font, + str.asOrderedText(), + (int) (childrenBadgeX + (float) childrenBadgeWidth / 2 - (float) childrenWidth / 2), + childrenBadgeY + 1, + 0xFFCACACA, + false + ); + + this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; + if (isMouseOver(mouseX, mouseY)) { + drawContext.fill(x, y, x + iconSize, y + iconSize, 0xA0909090); + int xOffset = list.getParent().showModChildren.contains(getMod().getId()) ? iconSize : 0; + int yOffset = hoveringIcon ? iconSize : 0; + drawContext.drawTexture( + RenderPipelines.GUI_TEXTURED, + PARENT_MOD_TEXTURE, + x, + y, + xOffset, + yOffset, + iconSize + xOffset, + iconSize + yOffset, + ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, + ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, + 0xFFFFFFFF + ); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int i) { + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + boolean quickConfigure = ModMenuConfig.QUICK_CONFIGURE.getValue(); + if (mouseX - list.getRowLeft() <= iconSize) { + this.toggleChildren(); + return true; + } else if (!quickConfigure && Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { + this.toggleChildren(); + return true; + } else { + return super.mouseClicked(mouseX, mouseY, i); + } + } + + private void toggleChildren() { + String id = getMod().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 keyCode, int scanCode, int modifiers) { + String modId = getMod().getId(); + if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE) { + if (list.getParent().showModChildren.contains(modId)) { + list.getParent().showModChildren.remove(modId); + } else { + list.getParent().showModChildren.add(modId); + } + + list.filter(list.getParent().getSearchInput(), false); + return true; + } else if (keyCode == GLFW.GLFW_KEY_LEFT) { + if (list.getParent().showModChildren.contains(modId)) { + list.getParent().showModChildren.remove(modId); + list.filter(list.getParent().getSearchInput(), false); + } + + return true; + } else if (keyCode == GLFW.GLFW_KEY_RIGHT) { + if (!list.getParent().showModChildren.contains(modId)) { + list.getParent().showModChildren.add(modId); + list.filter(list.getParent().getSearchInput(), false); + return true; + } else { + return list.keyPressed(GLFW.GLFW_KEY_DOWN, 0, 0); + } + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChildren(List children) { + this.children.addAll(children); + } + + public void addChildren(Mod... children) { + this.children.addAll(Arrays.asList(children)); + } + + public List getChildren() { + return children; + } + + @Override + public boolean isMouseOver(double double_1, double double_2) { + return Objects.equals(this.list.getEntryAtPos(double_1, double_2), this); + } } diff --git a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java index 535935d5..4306020d 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java @@ -18,98 +18,76 @@ @Environment(EnvType.CLIENT) public class DrawingUtil { - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - public static void drawRandomVersionBackground( - Mod mod, - DrawContext drawContext, - int x, - int y, - int width, - int height - ) { - int seed = mod.getName().hashCode() + mod.getVersion().hashCode(); + public static void drawRandomVersionBackground( + Mod mod, + DrawContext drawContext, + int x, + int y, + int width, + int height + ) { + int seed = mod.getName().hashCode() + mod.getVersion().hashCode(); - Random random = new Random(seed); - int color = 0xFF000000 | MathHelper.hsvToRgb(random.nextFloat(1f), random.nextFloat(0.7f, 0.8f), 0.9f); - if (!ModMenuConfig.RANDOM_JAVA_COLORS.getValue()) { - color = 0xFFDD5656; - } + Random random = new Random(seed); + int color = 0xFF000000 | MathHelper.hsvToRgb(random.nextFloat(1f), random.nextFloat(0.7f, 0.8f), 0.9f); + if (!ModMenuConfig.RANDOM_JAVA_COLORS.getValue()) { + color = 0xFFDD5656; + } - drawContext.fill(x, y, x + width, y + height, color); - } + drawContext.fill(x, y, x + width, y + height, color); + } - public static void drawWrappedString( - DrawContext drawContext, - String string, - int x, - int y, - int wrapWidth, - int lines, - int color - ) { - while (string != null && string.endsWith("\n")) { - string = string.substring(0, string.length() - 1); - } + public static void drawWrappedString( + DrawContext drawContext, + String string, + int x, + int y, + int wrapWidth, + int lines, + int color + ) { + while (string != null && string.endsWith("\n")) { + string = string.substring(0, string.length() - 1); + } -// drawContext.method_71046(); - List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); - for (int i = 0; i < strings.size(); i++) { - if (i >= lines) { - break; - } + List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); + for (int i = 0; i < strings.size(); i++) { + if (i >= lines) { + break; + } - StringVisitable renderable = strings.get(i); - if (i == lines - 1 && strings.size() > lines) { - renderable = StringVisitable.concat(strings.get(i), StringVisitable.plain("...")); - } + StringVisitable renderable = strings.get(i); + if (i == lines - 1 && strings.size() > lines) { + renderable = StringVisitable.concat(strings.get(i), StringVisitable.plain("...")); + } - OrderedText line = Language.getInstance().reorder(renderable); - int x1 = x; - if (CLIENT.textRenderer.isRightToLeft()) { - x1 += wrapWidth - CLIENT.textRenderer.getWidth(line); - } + OrderedText line = Language.getInstance().reorder(renderable); + int x1 = x; + if (CLIENT.textRenderer.isRightToLeft()) { + x1 += wrapWidth - CLIENT.textRenderer.getWidth(line); + } - drawContext.drawTextWithShadow( - CLIENT.textRenderer, - line, - x1, - y + i * CLIENT.textRenderer.fontHeight, - color - ); - } -// drawContext.method_71050(); - } + drawContext.drawTextWithShadow(CLIENT.textRenderer, line, x1, y + i * CLIENT.textRenderer.fontHeight, color); + } + } - public static void drawBadge( - DrawContext drawContext, - int x, - int y, - int tagWidth, - OrderedText text, - int outlineColor, - int fillColor, - int textColor - ) { - drawContext.fill(x + 1, y - 1, x + tagWidth, y, outlineColor); - drawContext.fill(x, y, x + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); - drawContext.fill(x + 1, - y + 1 + CLIENT.textRenderer.fontHeight - 1, - x + tagWidth, - y + CLIENT.textRenderer.fontHeight + 1, - outlineColor - ); - drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); - drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); -// drawContext.method_71046(); - drawContext.drawText( - CLIENT.textRenderer, - text, - (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), - y + 1, - textColor, - false - ); -// drawContext.method_71050(); - } + public static void drawBadge( + DrawContext drawContext, + int x, + int y, + int tagWidth, + OrderedText text, + int outlineColor, + int fillColor, + int textColor + ) { + drawContext.fill(x + 1, y - 1, x + tagWidth, y, outlineColor); + drawContext.fill(x, y, x + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); + drawContext.fill(x + 1, y + 1 + CLIENT.textRenderer.fontHeight - 1, x + tagWidth, y + CLIENT.textRenderer.fontHeight + 1, outlineColor); + drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); + drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); + drawContext.drawText(CLIENT.textRenderer, text, (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), y + 1, textColor, false); + } } diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java b/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java index ddba44fa..b5bd8ac9 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java @@ -138,38 +138,38 @@ default boolean hasUpdate() { enum Badge { LIBRARY( "modmenu.badge.library", - 0xff107454, - 0xff093929, + 0xFF107454, + 0xFF093929, "library" ), CLIENT( "modmenu.badge.clientsideOnly", - 0xff2b4b7c, - 0xff0e2a55, + 0xFF2b4b7c, + 0xFF0e2a55, null ), DEPRECATED( "modmenu.badge.deprecated", - 0xff841426, - 0xff530C17, + 0xFF841426, + 0xFF530C17, "deprecated" ), PATCHWORK_FORGE( "modmenu.badge.forge", - 0xff1f2d42, - 0xff101721, + 0xFF1f2d42, + 0xFF101721, null ), MODPACK( "modmenu.badge.modpack", - 0xff7a2b7c, - 0xff510d54, + 0xFF7a2b7c, + 0xFF510d54, null ), MINECRAFT( "modmenu.badge.minecraft", - 0xff6f6c6a, - 0xff31302f, + 0xFF6f6c6a, + 0xFF31302f, null ); diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java b/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java index d94326e8..19ae2588 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java @@ -9,50 +9,50 @@ import java.util.Set; public class ModBadgeRenderer { - protected int startX, startY, badgeX, badgeY, badgeMax; - protected Mod mod; - protected MinecraftClient client; - protected final ModsScreen screen; - - public ModBadgeRenderer(int startX, int startY, int endX, Mod mod, ModsScreen screen) { - this.startX = startX; - this.startY = startY; - this.badgeMax = endX; - this.mod = mod; - this.screen = screen; - this.client = MinecraftClient.getInstance(); - } - - public void draw(DrawContext drawContext, int mouseX, int mouseY) { - this.badgeX = startX; - this.badgeY = startY; - Set badges = mod.getBadges(); - badges.forEach(badge -> drawBadge(drawContext, badge, mouseX, mouseY)); - } - - public void drawBadge(DrawContext drawContext, Mod.Badge badge, int mouseX, int mouseY) { - this.drawBadge( - drawContext, - badge.getText().asOrderedText(), - badge.getOutlineColor(), - badge.getFillColor() - ); - } - - public void drawBadge( - DrawContext drawContext, - OrderedText text, - int outlineColor, - int fillColor - ) { - int width = client.textRenderer.getWidth(text) + 6; - if (badgeX + width < badgeMax) { - DrawingUtil.drawBadge(drawContext, badgeX, badgeY, width, text, outlineColor, fillColor, 0xCACACA); - badgeX += width + 3; - } - } - - public Mod getMod() { - return mod; - } + protected int startX, startY, badgeX, badgeY, badgeMax; + protected Mod mod; + protected MinecraftClient client; + protected final ModsScreen screen; + + public ModBadgeRenderer(int startX, int startY, int endX, Mod mod, ModsScreen screen) { + this.startX = startX; + this.startY = startY; + this.badgeMax = endX; + this.mod = mod; + this.screen = screen; + this.client = MinecraftClient.getInstance(); + } + + public void draw(DrawContext drawContext, int mouseX, int mouseY) { + this.badgeX = startX; + this.badgeY = startY; + Set badges = mod.getBadges(); + badges.forEach(badge -> drawBadge(drawContext, badge, mouseX, mouseY)); + } + + public void drawBadge(DrawContext drawContext, Mod.Badge badge, int mouseX, int mouseY) { + this.drawBadge( + drawContext, + badge.getText().asOrderedText(), + badge.getOutlineColor(), + badge.getFillColor() + ); + } + + public void drawBadge( + DrawContext drawContext, + OrderedText text, + int outlineColor, + int fillColor + ) { + int width = client.textRenderer.getWidth(text) + 6; + if (badgeX + width < badgeMax) { + DrawingUtil.drawBadge(drawContext, badgeX, badgeY, width, text, outlineColor, fillColor, 0xFFCACACA); + badgeX += width + 3; + } + } + + public Mod getMod() { + return mod; + } } From 66ceb89733d5c846d4899c4fe16e8e6c2a8064c3 Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Sat, 7 Jun 2025 17:37:49 -0400 Subject: [PATCH 5/8] Fix search box title text --- .../java/com/terraformersmc/modmenu/gui/ModsScreen.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index 55b406a0..21aa7bbb 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -1,7 +1,6 @@ package com.terraformersmc.modmenu.gui; import com.google.common.base.Joiner; -import com.mojang.blaze3d.opengl.GlStateManager; import com.terraformersmc.modmenu.ModMenu; import com.terraformersmc.modmenu.config.ModMenuConfig; import com.terraformersmc.modmenu.config.ModMenuConfigManager; @@ -123,7 +122,8 @@ protected void init() { int searchBoxWidth = ModMenuConfig.CONFIG_MODE.getValue() ? Math.min(200, searchWidthMax) : searchWidthMax; this.searchBoxX = this.paneWidth / 2 - searchBoxWidth / 2 - filtersButtonSize / 2; - this.searchBox = new TextFieldWidget(this.textRenderer, + this.searchBox = new TextFieldWidget( + this.textRenderer, this.searchBoxX, 22, searchBoxWidth, @@ -294,10 +294,9 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) this.modList.render(drawContext, mouseX, mouseY, delta); this.searchBox.render(drawContext, mouseX, mouseY, delta); - GlStateManager._disableBlend(); - drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.modList.getWidth() / 2, 8, 16777215); + drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.modList.getWidth() / 2, 8, 0xFFFFFFFF); assert client != null; - int grayColor = 11184810; + int grayColor = 0xFFAAAAAA; if (!ModMenuConfig.DISABLE_DRAG_AND_DROP.getValue()) { drawContext.drawCenteredTextWithShadow( this.textRenderer, From d27e7d14faec673fffdd56d14291677b12efd752 Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Sat, 7 Jun 2025 17:44:14 -0400 Subject: [PATCH 6/8] Fix colors --- .../modmenu/gui/ModsScreen.java | 10 +++----- .../gui/widget/DescriptionListWidget.java | 25 +++---------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index 21aa7bbb..374d43a5 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -433,7 +433,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) mod.getPrefixedVersion(), x + imageOffset, RIGHT_PANE_Y + 2 + lineSpacing, - 0xFF808080, + 0xFFAAAAAA, true ); } @@ -454,7 +454,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) RIGHT_PANE_Y + 2 + lineSpacing * 2, this.paneWidth - imageOffset - 4, 1, - 0xFF808080 + 0xFFAAAAAA ); } } @@ -526,13 +526,9 @@ public void updateSelectedEntry(ModListEntry entry) { String modId = selected.getMod().getId(); this.descriptionListWidget.updateSelectedModIfRequired(selected.getMod()); - if (this.configureButton != null) { - this.configureButton.active = getModHasConfigScreen(modId); - this.configureButton.visible = - selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId); - + this.configureButton.visible = selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId); if (modScreenErrors.containsKey(modId)) { Throwable e = modScreenErrors.get(modId); this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.configureError(modId, e))); diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java index 2bf3ccba..1c7e0ea9 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java @@ -117,7 +117,6 @@ private void rebuildUI() { UpdateInfo updateInfo = mod.getUpdateInfo(); if (updateInfo != null && updateInfo.isUpdateAvailable()) { children().add(emptyEntry); - int index = 0; for (OrderedText line : textRenderer.wrapLines(HAS_UPDATE_TEXT, wrapWidth - 11)) { DescriptionEntry entry = new DescriptionEntry(line); @@ -140,9 +139,7 @@ private void rebuildUI() { updateMessage = DOWNLOAD_TEXT; } else { if (downloadLink != null) { - updateMessage = updateMessage.copy() - .formatted(Formatting.BLUE) - .formatted(Formatting.UNDERLINE); + updateMessage = updateMessage.copy().formatted(Formatting.BLUE, Formatting.UNDERLINE); } } for (OrderedText line : textRenderer.wrapLines(updateMessage, wrapWidth - 16)) { @@ -189,11 +186,7 @@ private void rebuildUI() { links.forEach((key, value) -> { int indent = 8; - for (OrderedText line : textRenderer.wrapLines(Text.translatable(key) - .formatted(Formatting.BLUE) - .formatted(Formatting.UNDERLINE), - wrapWidth - 16 - )) { + for (OrderedText line : textRenderer.wrapLines(Text.translatable(key).formatted(Formatting.BLUE, Formatting.UNDERLINE), wrapWidth - 16)) { children().add(new LinkEntry(line, value, indent)); indent = 16; } @@ -203,7 +196,6 @@ private void rebuildUI() { Set licenses = mod.getLicense(); if (!ModMenuConfig.HIDE_MOD_LICENSE.getValue() && !licenses.isEmpty()) { children().add(emptyEntry); - for (OrderedText line : textRenderer.wrapLines(LICENSE_TEXT, wrapWidth)) { children().add(new DescriptionEntry(line)); } @@ -220,38 +212,28 @@ private void rebuildUI() { if (!ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { if ("minecraft".equals(mod.getId())) { children().add(emptyEntry); - for (OrderedText line : textRenderer.wrapLines(VIEW_CREDITS_TEXT, wrapWidth)) { children().add(new MojangCreditsEntry(line)); } } else if (!"java".equals(mod.getId())) { var credits = mod.getCredits(); - if (!credits.isEmpty()) { children().add(emptyEntry); - for (OrderedText line : textRenderer.wrapLines(CREDITS_TEXT, wrapWidth)) { children().add(new DescriptionEntry(line)); } var iterator = credits.entrySet().iterator(); - while (iterator.hasNext()) { int indent = 8; - var role = iterator.next(); - - - for (var line : textRenderer.wrapLines(this.creditsRoleText(role.getKey()), - wrapWidth - 16 - )) { + for (var line : textRenderer.wrapLines(this.creditsRoleText(role.getKey()), wrapWidth - 16)) { children().add(new DescriptionEntry(line, indent)); indent = 16; } for (var contributor : role.getValue()) { indent = 16; - for (var line : textRenderer.wrapLines(Text.literal(contributor), wrapWidth - 24)) { children().add(new DescriptionEntry(line, indent)); indent = 24; @@ -281,7 +263,6 @@ public void renderList(DrawContext drawContext, int mouseX, int mouseY, float de this.enableScissor(drawContext); super.renderList(drawContext, mouseX, mouseY, delta); drawContext.disableScissor(); - RenderPipeline pipeline = RenderPipelines.GUI; try (BufferAllocator alloc = new BufferAllocator(pipeline.getVertexFormat().getVertexSize() * 4)) { BufferBuilder bufferBuilder = new BufferBuilder(alloc, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); From 143f10f65c365d37c1129f7abbe8e61b8efc22de Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Sat, 7 Jun 2025 17:49:41 -0400 Subject: [PATCH 7/8] Require >=1.21.6-pre3 to launch --- src/main/resources/fabric.mod.json | 220 ++++++++++++++--------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index f82cc34c..5c85a05c 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,112 +1,112 @@ { - "schemaVersion": 1, - "id": "modmenu", - "name": "Mod Menu", - "version": "$version", - "environment": "client", - "license": "MIT", - "icon": "assets/modmenu/icon.png", - "entrypoints": { - "client": [ - "com.terraformersmc.modmenu.ModMenu" - ], - "modmenu": [ - "com.terraformersmc.modmenu.ModMenuModMenuCompat" - ] - }, - "contact": { - "homepage": "https://modrinth.com/mod/modmenu", - "sources": "https://github.com/TerraformersMC/ModMenu", - "issues": "https://github.com/TerraformersMC/ModMenu/issues" - }, - "depends": { - "fabric-resource-loader-v0": "*", - "fabric-screen-api-v1": "*", - "fabric-key-binding-api-v1": "*", - "fabric-lifecycle-events-v1": "*", - "fabricloader": ">=0.16.10", - "minecraft": ">=1.21.5-rc.1" - }, - "authors": [ - "Prospector", - "haykam821", - "TerraformersMC" - ], - "contributors": [ - "shedaniel", - "Draylar", - "Mordna", - "Madis0", - "Yanis48", - "LemmaEOF", - "fewizz", - "XuyuEre", - "geniiii", - "thiagokenis", - "Juuxel", - "Chloe Dawn", - "modmuss50", - "Patbox", - "Lykrast", - "Pyrofab", - "UpcraftLP", - "swordglowsblue", - "hinataaki", - "LeonXu98", - "magneticflux", - "vanja-san", - "AlexIIL", - "masoncook16", - "el97", - "Hephaestus-Dev", - "zabi94", - "dexman545", - "dhouck", - "Hambaka", - "po-stulate", - "FlashyReese", - "egeesin", - "TheGlitch76", - "ludg1e", - "williambl", - "PepperCode1", - "ThatTrollzer", - "ToffeeMax", - "kalucky0", - "Maaster", - "MartrixX", - "NotSteven", - "Dolphin 2.1", - "ENDERZOMBI102", - "anatom3000", - "OroArmor", - "jackassmc", - "Vaerian", - "RDKRACZ", - "Hulenkius", - "XfedeX", - "spnda", - "Jab125", - "SolidBlock", - "Tkain", - "nfitzen", - "DenaryDev", - "legenden", - "NaiNonTH", - "MagnusHJensen", - "Benjamin-Norton", - "triphora", - "Pyrrha" - ], - "description": "Adds a mod menu to view the list of mods you have installed.", - "mixins": [ - "mixins.modmenu.json" - ], - "custom": { - "modmenu": { - "links": { - "modmenu.discord": "https://discord.gg/jEGF5fb" - } - } - } + "schemaVersion": 1, + "id": "modmenu", + "name": "Mod Menu", + "version": "$version", + "environment": "client", + "license": "MIT", + "icon": "assets/modmenu/icon.png", + "entrypoints": { + "client": [ + "com.terraformersmc.modmenu.ModMenu" + ], + "modmenu": [ + "com.terraformersmc.modmenu.ModMenuModMenuCompat" + ] + }, + "contact": { + "homepage": "https://modrinth.com/mod/modmenu", + "sources": "https://github.com/TerraformersMC/ModMenu", + "issues": "https://github.com/TerraformersMC/ModMenu/issues" + }, + "depends": { + "fabric-resource-loader-v0": "*", + "fabric-screen-api-v1": "*", + "fabric-key-binding-api-v1": "*", + "fabric-lifecycle-events-v1": "*", + "fabricloader": ">=0.16.10", + "minecraft": "~1.21.6-beta.3" + }, + "authors": [ + "Prospector", + "haykam821", + "TerraformersMC" + ], + "contributors": [ + "shedaniel", + "Draylar", + "Mordna", + "Madis0", + "Yanis48", + "LemmaEOF", + "fewizz", + "XuyuEre", + "geniiii", + "thiagokenis", + "Juuxel", + "Chloe Dawn", + "modmuss50", + "Patbox", + "Lykrast", + "Pyrofab", + "UpcraftLP", + "swordglowsblue", + "hinataaki", + "LeonXu98", + "magneticflux", + "vanja-san", + "AlexIIL", + "masoncook16", + "el97", + "Hephaestus-Dev", + "zabi94", + "dexman545", + "dhouck", + "Hambaka", + "po-stulate", + "FlashyReese", + "egeesin", + "TheGlitch76", + "ludg1e", + "williambl", + "PepperCode1", + "ThatTrollzer", + "ToffeeMax", + "kalucky0", + "Maaster", + "MartrixX", + "NotSteven", + "Dolphin 2.1", + "ENDERZOMBI102", + "anatom3000", + "OroArmor", + "jackassmc", + "Vaerian", + "RDKRACZ", + "Hulenkius", + "XfedeX", + "spnda", + "Jab125", + "SolidBlock", + "Tkain", + "nfitzen", + "DenaryDev", + "legenden", + "NaiNonTH", + "MagnusHJensen", + "Benjamin-Norton", + "triphora", + "Pyrrha" + ], + "description": "Adds a mod menu to view the list of mods you have installed.", + "mixins": [ + "mixins.modmenu.json" + ], + "custom": { + "modmenu": { + "links": { + "modmenu.discord": "https://discord.gg/jEGF5fb" + } + } + } } From 7148a26ee24e2a6d6d2ada8638cc40835076488e Mon Sep 17 00:00:00 2001 From: gniftygnome Date: Mon, 16 Jun 2025 18:23:33 -0700 Subject: [PATCH 8/8] Revert code style changes so a squash merge will give a clean diff. * Revert code style changes - Update to 1.21.6-rc1 --- build.gradle | 2 +- gradle.properties | 11 +- .../modmenu/gui/ModsScreen.java | 1198 +++++++++-------- .../gui/widget/DescriptionListWidget.java | 819 +++++------ .../widget/LegacyTexturedButtonWidget.java | 2 +- .../modmenu/gui/widget/ModListWidget.java | 45 +- .../gui/widget/entries/ModListEntry.java | 381 +++--- .../gui/widget/entries/ParentEntry.java | 368 ++--- .../modmenu/util/DrawingUtil.java | 137 +- .../modmenu/util/mod/ModBadgeRenderer.java | 96 +- src/main/resources/fabric.mod.json | 220 +-- 11 files changed, 1657 insertions(+), 1622 deletions(-) diff --git a/build.gradle b/build.gradle index c9ad371f..c55b5ddf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version "${loom_version}" + id 'fabric-loom' version '1.10-SNAPSHOT' } apply from: 'https://raw.githubusercontent.com/TerraformersMC/GradleScripts/2.7/ferry.gradle' diff --git a/gradle.properties b/gradle.properties index 8aae7349..4285f4a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,13 +4,12 @@ org.gradle.parallel=true maven_group=com.terraformersmc archive_name=modmenu -minecraft_version=1.21.6-pre3 -yarn_mappings=1.21.6-pre3+build.2 +minecraft_version=1.21.6-rc1 +yarn_mappings=1.21.6-rc1+build.1 loader_version=0.16.14 -fabric_version=0.126.0+1.21.6 +fabric_version=0.127.0+1.21.6 text_placeholder_api_version=2.7.0+1.21.6 quilt_loader_version=0.29.0-beta.3 -loom_version=1.10-SNAPSHOT # Project Metadata project_name=Mod Menu @@ -23,14 +22,14 @@ default_release_type=stable # Modrinth Metadata modrinth_slug=modmenu modrinth_id=mOgUt4GM -modrinth_game_versions= +modrinth_game_versions=1.21.6-rc1 modrinth_mod_loaders=fabric, quilt modrinth_required_dependencies=fabric-api, placeholder-api # CurseForge Metadata curseforge_slug=modmenu curseforge_id=308702 -curseforge_game_versions=Fabric, Quilt +curseforge_game_versions=1.21.6-snapshot, Fabric, Quilt curseforge_required_dependencies=fabric-api, text-placeholder-api curseforge_optional_dependencies= diff --git a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java index 374d43a5..118d99a7 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/ModsScreen.java @@ -45,602 +45,604 @@ import java.util.stream.Collectors; public class ModsScreen extends Screen { - private static final Identifier FILTERS_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/filters_button.png"); - private static final Identifier CONFIGURE_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/configure_button.png"); - - private static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu | ModsScreen"); - private final Screen previousScreen; - private ModListEntry selected; - private ModBadgeRenderer modBadgeRenderer; - private double scrollPercent = 0; - private boolean keepFilterOptionsShown = false; - private boolean init = false; - private boolean filterOptionsShown = false; - private static final int RIGHT_PANE_Y = 48; - private int paneWidth; - private int rightPaneX; - private int searchBoxX; - private int filtersX; - private int filtersWidth; - private int searchRowWidth; - public final Set showModChildren = new HashSet<>(); - - private TextFieldWidget searchBox; - private @Nullable ClickableWidget filtersButton; - private ClickableWidget sortingButton; - private ClickableWidget librariesButton; - private ModListWidget modList; - private @Nullable ClickableWidget configureButton; - private ClickableWidget websiteButton; - private ClickableWidget issuesButton; - private DescriptionListWidget descriptionListWidget; - - public final Map modHasConfigScreen = new HashMap<>(); - public final Map modScreenErrors = new HashMap<>(); - - private static final Text SEND_FEEDBACK_TEXT = Text.translatable("menu.sendFeedback"); - private static final Text REPORT_BUGS_TEXT = Text.translatable("menu.reportBugs"); - - public ModsScreen(Screen previousScreen) { - super(ModMenuScreenTexts.TITLE); - this.previousScreen = previousScreen; - } - - @Override - public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - if (modList.isMouseOver(mouseX, mouseY)) { - return this.modList.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); - } - - if (descriptionListWidget.isMouseOver(mouseX, mouseY)) { - return this.descriptionListWidget.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); - } - - return false; - } - - @Override - protected void init() { - int paneY = ModMenuConfig.CONFIG_MODE.getValue() ? 48 : 48 + 19; - this.paneWidth = this.width / 2 - 8; - this.rightPaneX = this.width - this.paneWidth; - - // Mod list (initialized early for updateFiltersX) - this.modList = new ModListWidget(this.client, - this.paneWidth, - this.height - paneY - 36, - paneY, - ModMenuConfig.COMPACT_LIST.getValue() ? 23 : 36, - this.modList, - this - ); - this.modList.setX(0); - - // Search box - int filtersButtonSize = (ModMenuConfig.CONFIG_MODE.getValue() ? 0 : 22); - int searchWidthMax = this.paneWidth - 32 - filtersButtonSize; - int searchBoxWidth = ModMenuConfig.CONFIG_MODE.getValue() ? Math.min(200, searchWidthMax) : searchWidthMax; - - this.searchBoxX = this.paneWidth / 2 - searchBoxWidth / 2 - filtersButtonSize / 2; - this.searchBox = new TextFieldWidget( - this.textRenderer, - this.searchBoxX, - 22, - searchBoxWidth, - 20, - this.searchBox, - ModMenuScreenTexts.SEARCH - ); - this.searchBox.setChangedListener(text -> { - this.modList.filter(text, false); - }); - - // Filters button - Text sortingText = ModMenuConfig.SORTING.getButtonText(); - Text librariesText = ModMenuConfig.SHOW_LIBRARIES.getButtonText(); - - int sortingWidth = textRenderer.getWidth(sortingText) + 28; - int librariesWidth = textRenderer.getWidth(librariesText) + 20; - - this.filtersWidth = librariesWidth + sortingWidth + 2; - this.searchRowWidth = this.searchBoxX + searchBoxWidth + 22; - - this.updateFiltersX(true); - - if (!ModMenuConfig.CONFIG_MODE.getValue()) { - this.filtersButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS, - button -> { - this.setFilterOptionsShown(!this.filterOptionsShown); - } - ) - .position(this.paneWidth / 2 + searchBoxWidth / 2 - 20 / 2 + 2, 22) - .size(20, 20) - .uv(0, 0, 20) - .texture(FILTERS_BUTTON_LOCATION, 32, 64) - .build(); - - this.filtersButton.setTooltip(Tooltip.of(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS)); - } - - // Sorting button - this.sortingButton = ButtonWidget.builder(sortingText, button -> { - ModMenuConfig.SORTING.cycleValue(); - ModMenuConfigManager.save(); - modList.reloadFilters(); - button.setMessage(ModMenuConfig.SORTING.getButtonText()); - }).position(this.filtersX, 45).size(sortingWidth, 20).build(); - - // Show libraries button - this.librariesButton = ButtonWidget.builder(librariesText, button -> { - ModMenuConfig.SHOW_LIBRARIES.toggleValue(); - ModMenuConfigManager.save(); - modList.reloadFilters(); - button.setMessage(ModMenuConfig.SHOW_LIBRARIES.getButtonText()); - }).position(this.filtersX + sortingWidth + 2, 45).size(librariesWidth, 20).build(); - - // Configure button - if (!ModMenuConfig.HIDE_CONFIG_BUTTONS.getValue()) { - this.configureButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ScreenTexts.EMPTY, button -> { - final String id = Objects.requireNonNull(selected).getMod().getId(); - if (getModHasConfigScreen(id)) { - this.safelyOpenConfigScreen(id); - } else { - button.active = false; - } - }) - .position(width - 24, RIGHT_PANE_Y) - .size(20, 20) - .uv(0, 0, 20) - .texture(CONFIGURE_BUTTON_LOCATION, 32, 64) - .build(); - } - - // Website button - int urlButtonWidths = this.paneWidth / 2 - 2; - int cappedButtonWidth = Math.min(urlButtonWidths, 200); - this.websiteButton = ButtonWidget.builder(ModMenuScreenTexts.WEBSITE, button -> { - final Mod mod = Objects.requireNonNull(selected).getMod(); - boolean isMinecraft = selected.getMod().getId().equals("minecraft"); - if (isMinecraft) { - var url = SharedConstants.getGameVersion().stable() ? Urls.JAVA_FEEDBACK : Urls.SNAPSHOT_FEEDBACK; - ConfirmLinkScreen.open(this, url, true); - } else { - var url = mod.getWebsite(); - ConfirmLinkScreen.open(this, url, false); - } - }) - .position(this.rightPaneX + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) - .size(Math.min(urlButtonWidths, 200), 20) - .build(); - - // Issues button - this.issuesButton = ButtonWidget.builder(ModMenuScreenTexts.ISSUES, button -> { - final Mod mod = Objects.requireNonNull(selected).getMod(); - boolean isMinecraft = selected.getMod().getId().equals("minecraft"); - if (isMinecraft) { - ConfirmLinkScreen.open(this, Urls.SNAPSHOT_BUGS, true); - } else { - var url = mod.getIssueTracker(); - ConfirmLinkScreen.open(this, url, false); - } - }) - .position(this.rightPaneX + urlButtonWidths + 4 + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) - .size(Math.min(urlButtonWidths, 200), 20) - .build(); - - // Description list - this.descriptionListWidget = new DescriptionListWidget( - this.client, - this.paneWidth, - this.height - RIGHT_PANE_Y - 96, - RIGHT_PANE_Y + 60, - textRenderer.fontHeight + 1, - this.descriptionListWidget, - this - ); - this.descriptionListWidget.setX(this.rightPaneX); - - // Mods folder button - ClickableWidget modsFolderButton = ButtonWidget.builder(ModMenuScreenTexts.MODS_FOLDER, button -> Util.getOperatingSystem().open(FabricLoader.getInstance().getGameDir().resolve("mods").toUri())).position(this.width / 2 - 154, this.height - 28).size(150, 20).build(); - - // Done button - ClickableWidget doneButton = ButtonWidget.builder(ScreenTexts.DONE, button -> client.setScreen(previousScreen)).position(this.width / 2 + 4, this.height - 28).size(150, 20).build(); - - // Initialize data - modList.finalizeInit(); - this.setFilterOptionsShown(this.keepFilterOptionsShown && this.filterOptionsShown); - - // Add children - this.addSelectableChild(this.searchBox); - this.setInitialFocus(this.searchBox); - if (this.filtersButton != null) { - this.addDrawableChild(this.filtersButton); - } - - this.addDrawableChild(this.sortingButton); - this.addDrawableChild(this.librariesButton); - this.addSelectableChild(this.modList); - if (this.configureButton != null) { - this.addDrawableChild(this.configureButton); - } - - this.addDrawableChild(this.websiteButton); - this.addDrawableChild(this.issuesButton); - this.addSelectableChild(this.descriptionListWidget); - this.addDrawableChild(modsFolderButton); - this.addDrawableChild(doneButton); - - this.init = true; - this.keepFilterOptionsShown = true; - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - return super.keyPressed(keyCode, scanCode, modifiers) || this.searchBox.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean charTyped(char chr, int keyCode) { - return this.searchBox.charTyped(chr, keyCode); - } - - @Override - public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) { - super.render(drawContext, mouseX, mouseY, delta); - ModListEntry selectedEntry = selected; - if (selectedEntry != null) { - this.descriptionListWidget.render(drawContext, mouseX, mouseY, delta); - } - - this.modList.render(drawContext, mouseX, mouseY, delta); - this.searchBox.render(drawContext, mouseX, mouseY, delta); - drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.modList.getWidth() / 2, 8, 0xFFFFFFFF); - assert client != null; - int grayColor = 0xFFAAAAAA; - if (!ModMenuConfig.DISABLE_DRAG_AND_DROP.getValue()) { - drawContext.drawCenteredTextWithShadow( - this.textRenderer, - ModMenuScreenTexts.DROP_INFO_LINE_1, - this.width - this.modList.getWidth() / 2, - RIGHT_PANE_Y / 2 - client.textRenderer.fontHeight - 1, - grayColor - ); - drawContext.drawCenteredTextWithShadow( - this.textRenderer, - ModMenuScreenTexts.DROP_INFO_LINE_2, - this.width - this.modList.getWidth() / 2, - RIGHT_PANE_Y / 2 + 1, - grayColor - ); - } - - if (!ModMenuConfig.CONFIG_MODE.getValue()) { - Text fullModCount = this.computeModCountText(true, false); - if (!ModMenuConfig.CONFIG_MODE.getValue() && this.updateFiltersX(false)) { - if (this.filterOptionsShown) { - if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || - textRenderer.getWidth(fullModCount) <= this.filtersX - 5) { - drawContext.drawText( - textRenderer, - fullModCount.asOrderedText(), - this.searchBoxX, - 52, - 0xFFFFFFFF, - true - ); - } else { - drawContext.drawText( - textRenderer, - computeModCountText(false, false).asOrderedText(), - this.searchBoxX, - 46, - 0xFFFFFFFF, - true - ); - drawContext.drawText( - textRenderer, - computeLibraryCountText(false).asOrderedText(), - this.searchBoxX, - 57, - 0xFFFFFFFF, - true - ); - } - } else { - if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || - textRenderer.getWidth(fullModCount) <= modList.getWidth() - 5) { - drawContext.drawText( - textRenderer, - fullModCount.asOrderedText(), - this.searchBoxX, - 52, - 0xFFFFFFFF, - true - ); - } else { - drawContext.drawText( - textRenderer, - computeModCountText(false, false).asOrderedText(), - this.searchBoxX, - 46, - 0xFFFFFFFF, - true - ); - drawContext.drawText( - textRenderer, - computeLibraryCountText(false).asOrderedText(), - this.searchBoxX, - 57, - 0xFFFFFFFF, - true - ); - } - } - } - } - - if (selectedEntry != null) { - Mod mod = selectedEntry.getMod(); - int x = this.rightPaneX; - if ("java".equals(mod.getId())) { - DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, RIGHT_PANE_Y, 32, 32); - } - - drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32, 0xFFFFFFFF); - int lineSpacing = textRenderer.fontHeight + 1; - int imageOffset = 36; - Text name = Text.literal(mod.getTranslatedName()); - StringVisitable trimmedName = name; - int maxNameWidth = this.width - (x + imageOffset); - if (textRenderer.getWidth(name) > maxNameWidth) { - StringVisitable ellipsis = StringVisitable.plain("..."); - trimmedName = StringVisitable.concat(textRenderer.trimToWidth(name, maxNameWidth - textRenderer.getWidth(ellipsis)), ellipsis); - } - - drawContext.drawText( - textRenderer, - Language.getInstance().reorder(trimmedName), - x + imageOffset, - RIGHT_PANE_Y + 1, - 0xFFFFFFFF, - true - ); - - if (mouseX > x + imageOffset && mouseY > RIGHT_PANE_Y + 1 && - mouseY < RIGHT_PANE_Y + 1 + textRenderer.fontHeight && - mouseX < x + imageOffset + textRenderer.getWidth(trimmedName)) { - drawContext.drawTooltip(ModMenuScreenTexts.modIdTooltip(mod.getId()), mouseX, mouseY); - } - - if (this.init || modBadgeRenderer == null || modBadgeRenderer.getMod() != mod) { - modBadgeRenderer = new ModBadgeRenderer( - x + imageOffset + client.textRenderer.getWidth(trimmedName) + 2, - RIGHT_PANE_Y, - width - 28, - selectedEntry.mod, - this - ); - this.init = false; - } - - if (!ModMenuConfig.HIDE_BADGES.getValue()) { - modBadgeRenderer.draw(drawContext, mouseX, mouseY); - } - - if (mod.isReal()) { - drawContext.drawText( - textRenderer, - mod.getPrefixedVersion(), - x + imageOffset, - RIGHT_PANE_Y + 2 + lineSpacing, - 0xFFAAAAAA, - true - ); - } - - String authors; - List names = mod.getAuthors(); - if (!names.isEmpty()) { - if (names.size() > 1) { - authors = Joiner.on(", ").join(names); - } else { - authors = names.getFirst(); - } - - DrawingUtil.drawWrappedString( - drawContext, - I18n.translate("modmenu.authorPrefix", authors), - x + imageOffset, - RIGHT_PANE_Y + 2 + lineSpacing * 2, - this.paneWidth - imageOffset - 4, - 1, - 0xFFAAAAAA - ); - } - } - } - - private Text computeModCountText(boolean includeLibs, boolean onInit) { - int[] rootMods = formatModCount(ModMenu.ROOT_MODS.values() - .stream() - .filter(mod -> !mod.isHidden() && !mod.getBadges().contains(Mod.Badge.LIBRARY)) - .map(Mod::getId) - .collect(Collectors.toSet()), onInit); - if (includeLibs && ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { - int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() - .stream() - .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) - .map(Mod::getId) - .collect(Collectors.toSet()), false); - return TranslationUtil.translateNumeric("modmenu.showingModsLibraries", rootMods, rootLibs); - } else { - return TranslationUtil.translateNumeric("modmenu.showingMods", rootMods); - } - } - - private Text computeLibraryCountText(boolean onInit) { - if (ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { - int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() - .stream() - .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) - .map(Mod::getId) - .collect(Collectors.toSet()), false); - return TranslationUtil.translateNumeric("modmenu.showingLibraries", rootLibs); - } else { - return Text.empty(); - } - } - - 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}; - } - } - - @Override - public void close() { - this.modList.close(); - this.client.setScreen(this.previousScreen); - } - - private void setFilterOptionsShown(boolean filterOptionsShown) { - this.filterOptionsShown = filterOptionsShown; - this.sortingButton.visible = filterOptionsShown; - this.librariesButton.visible = filterOptionsShown; - } - - public ModListEntry getSelectedEntry() { - return selected; - } - - public void updateSelectedEntry(ModListEntry entry) { - if (entry == null) { - return; - } - - this.selected = entry; - String modId = selected.getMod().getId(); - - this.descriptionListWidget.updateSelectedModIfRequired(selected.getMod()); - if (this.configureButton != null) { - this.configureButton.active = getModHasConfigScreen(modId); - this.configureButton.visible = selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId); - if (modScreenErrors.containsKey(modId)) { - Throwable e = modScreenErrors.get(modId); - this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.configureError(modId, e))); - } else { - this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.CONFIGURE)); - } - } - - boolean isMinecraft = modId.equals("minecraft"); - this.websiteButton.setMessage(isMinecraft ? SEND_FEEDBACK_TEXT : ModMenuScreenTexts.WEBSITE); - this.issuesButton.setMessage(isMinecraft ? REPORT_BUGS_TEXT : ModMenuScreenTexts.ISSUES); - - this.websiteButton.visible = true; - this.websiteButton.active = isMinecraft || selected.getMod().getWebsite() != null; - - this.issuesButton.visible = true; - this.issuesButton.active = isMinecraft || selected.getMod().getIssueTracker() != null; - } - - public double getScrollPercent() { - return scrollPercent; - } - - public void updateScrollPercent(double scrollPercent) { - this.scrollPercent = scrollPercent; - } - - public String getSearchInput() { - return this.searchBox.getText(); - } - - private boolean updateFiltersX(boolean onInit) { - if ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(true, onInit)) + 20) >= this.searchRowWidth && - ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(false, onInit)) + 20) >= this.searchRowWidth || - (this.filtersWidth + textRenderer.getWidth(this.computeLibraryCountText(onInit)) + 20) >= this.searchRowWidth - )) { - this.filtersX = this.paneWidth / 2 - this.filtersWidth / 2; - return !filterOptionsShown; - } else { - this.filtersX = this.searchRowWidth - this.filtersWidth + 1; - return true; - } - } - - @Override - public void onFilesDropped(List paths) { - Path modsDirectory = FabricLoader.getInstance().getGameDir().resolve("mods"); - - // Filter out none mods - List mods = paths.stream().filter(ModsScreen::isValidMod).toList(); - if (mods.isEmpty()) { - return; - } - - String modList = mods.stream().map(Path::getFileName).map(Path::toString).collect(Collectors.joining(", ")); - assert this.client != null; - this.client.setScreen(new ConfirmScreen((value) -> { - if (value) { - boolean allSuccessful = true; - for (Path path : mods) { - try { - Files.copy(path, modsDirectory.resolve(path.getFileName())); - } catch (IOException e) { - LOGGER.warn("Failed to copy mod from {} to {}", path, modsDirectory.resolve(path.getFileName())); - SystemToast.addPackCopyFailure(client, path.toString()); - allSuccessful = false; - break; - } - } - - if (allSuccessful) { - SystemToast.add(client.getToastManager(), SystemToast.Type.PERIODIC_NOTIFICATION, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_1, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_2); - } - } - this.client.setScreen(this); - }, ModMenuScreenTexts.DROP_CONFIRM, Text.literal(modList))); - } - - private static boolean isValidMod(Path mod) { - try (JarFile jarFile = new JarFile(mod.toFile())) { - var isFabricMod = jarFile.getEntry("fabric.mod.json") != null; - if (!ModMenu.RUNNING_QUILT) { - return isFabricMod; - } else { - return isFabricMod || jarFile.getEntry("quilt.mod.json") != null; - } - } catch (IOException e) { - return false; - } - } - - public boolean getModHasConfigScreen(String modId) { - if (this.modScreenErrors.containsKey(modId)) { - return false; - } else { - return this.modHasConfigScreen.computeIfAbsent(modId, ModMenu::hasConfigScreen); - } - } - - public void safelyOpenConfigScreen(String modId) { - try { - Screen screen = ModMenu.getConfigScreen(modId, this); - if (screen != null) { - assert this.client != null; - this.client.setScreen(screen); - } - } catch (java.lang.NoClassDefFoundError e) { - LOGGER.warn("The '{}' mod config screen is not available because {} is missing.", modId, e.getLocalizedMessage()); - modScreenErrors.put(modId, e); - } catch (Throwable e) { - LOGGER.error("Error from mod '{}'", modId, e); - modScreenErrors.put(modId, e); - } - } + private static final Identifier FILTERS_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/filters_button.png"); + private static final Identifier CONFIGURE_BUTTON_LOCATION = Identifier.of(ModMenu.MOD_ID, "textures/gui/configure_button.png"); + + private static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu | ModsScreen"); + private final Screen previousScreen; + private ModListEntry selected; + private ModBadgeRenderer modBadgeRenderer; + private double scrollPercent = 0; + private boolean keepFilterOptionsShown = false; + private boolean init = false; + private boolean filterOptionsShown = false; + private static final int RIGHT_PANE_Y = 48; + private int paneWidth; + private int rightPaneX; + private int searchBoxX; + private int filtersX; + private int filtersWidth; + private int searchRowWidth; + public final Set showModChildren = new HashSet<>(); + + private TextFieldWidget searchBox; + private @Nullable ClickableWidget filtersButton; + private ClickableWidget sortingButton; + private ClickableWidget librariesButton; + private ModListWidget modList; + private @Nullable ClickableWidget configureButton; + private ClickableWidget websiteButton; + private ClickableWidget issuesButton; + private DescriptionListWidget descriptionListWidget; + + public final Map modHasConfigScreen = new HashMap<>(); + public final Map modScreenErrors = new HashMap<>(); + + private static final Text SEND_FEEDBACK_TEXT = Text.translatable("menu.sendFeedback"); + private static final Text REPORT_BUGS_TEXT = Text.translatable("menu.reportBugs"); + + public ModsScreen(Screen previousScreen) { + super(ModMenuScreenTexts.TITLE); + this.previousScreen = previousScreen; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { + if (modList.isMouseOver(mouseX, mouseY)) { + return this.modList.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); + } + + if (descriptionListWidget.isMouseOver(mouseX, mouseY)) { + return this.descriptionListWidget.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); + } + + return false; + } + + @Override + protected void init() { + int paneY = ModMenuConfig.CONFIG_MODE.getValue() ? 48 : 48 + 19; + this.paneWidth = this.width / 2 - 8; + this.rightPaneX = this.width - this.paneWidth; + + // Mod list (initialized early for updateFiltersX) + this.modList = new ModListWidget(this.client, + this.paneWidth, + this.height - paneY - 36, + paneY, + ModMenuConfig.COMPACT_LIST.getValue() ? 23 : 36, + this.modList, + this + ); + this.modList.setX(0); + + // Search box + int filtersButtonSize = (ModMenuConfig.CONFIG_MODE.getValue() ? 0 : 22); + int searchWidthMax = this.paneWidth - 32 - filtersButtonSize; + int searchBoxWidth = ModMenuConfig.CONFIG_MODE.getValue() ? Math.min(200, searchWidthMax) : searchWidthMax; + + this.searchBoxX = this.paneWidth / 2 - searchBoxWidth / 2 - filtersButtonSize / 2; + this.searchBox = new TextFieldWidget(this.textRenderer, + this.searchBoxX, + 22, + searchBoxWidth, + 20, + this.searchBox, + ModMenuScreenTexts.SEARCH + ); + this.searchBox.setChangedListener(text -> { + this.modList.filter(text, false); + }); + + // Filters button + Text sortingText = ModMenuConfig.SORTING.getButtonText(); + Text librariesText = ModMenuConfig.SHOW_LIBRARIES.getButtonText(); + + int sortingWidth = textRenderer.getWidth(sortingText) + 28; + int librariesWidth = textRenderer.getWidth(librariesText) + 20; + + this.filtersWidth = librariesWidth + sortingWidth + 2; + this.searchRowWidth = this.searchBoxX + searchBoxWidth + 22; + + this.updateFiltersX(true); + + if (!ModMenuConfig.CONFIG_MODE.getValue()) { + this.filtersButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS, + button -> { + this.setFilterOptionsShown(!this.filterOptionsShown); + } + ) + .position(this.paneWidth / 2 + searchBoxWidth / 2 - 20 / 2 + 2, 22) + .size(20, 20) + .uv(0, 0, 20) + .texture(FILTERS_BUTTON_LOCATION, 32, 64) + .build(); + + this.filtersButton.setTooltip(Tooltip.of(ModMenuScreenTexts.TOGGLE_FILTER_OPTIONS)); + } + + // Sorting button + this.sortingButton = ButtonWidget.builder(sortingText, button -> { + ModMenuConfig.SORTING.cycleValue(); + ModMenuConfigManager.save(); + modList.reloadFilters(); + button.setMessage(ModMenuConfig.SORTING.getButtonText()); + }).position(this.filtersX, 45).size(sortingWidth, 20).build(); + + // Show libraries button + this.librariesButton = ButtonWidget.builder(librariesText, button -> { + ModMenuConfig.SHOW_LIBRARIES.toggleValue(); + ModMenuConfigManager.save(); + modList.reloadFilters(); + button.setMessage(ModMenuConfig.SHOW_LIBRARIES.getButtonText()); + }).position(this.filtersX + sortingWidth + 2, 45).size(librariesWidth, 20).build(); + + // Configure button + if (!ModMenuConfig.HIDE_CONFIG_BUTTONS.getValue()) { + this.configureButton = LegacyTexturedButtonWidget.legacyTexturedBuilder(ScreenTexts.EMPTY, button -> { + final String id = Objects.requireNonNull(selected).getMod().getId(); + if (getModHasConfigScreen(id)) { + this.safelyOpenConfigScreen(id); + } else { + button.active = false; + } + }) + .position(width - 24, RIGHT_PANE_Y) + .size(20, 20) + .uv(0, 0, 20) + .texture(CONFIGURE_BUTTON_LOCATION, 32, 64) + .build(); + } + + // Website button + int urlButtonWidths = this.paneWidth / 2 - 2; + int cappedButtonWidth = Math.min(urlButtonWidths, 200); + this.websiteButton = ButtonWidget.builder(ModMenuScreenTexts.WEBSITE, button -> { + final Mod mod = Objects.requireNonNull(selected).getMod(); + boolean isMinecraft = selected.getMod().getId().equals("minecraft"); + if (isMinecraft) { + var url = SharedConstants.getGameVersion().stable() ? Urls.JAVA_FEEDBACK : Urls.SNAPSHOT_FEEDBACK; + ConfirmLinkScreen.open(this, url, true); + } else { + var url = mod.getWebsite(); + ConfirmLinkScreen.open(this, url, false); + } + }) + .position(this.rightPaneX + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) + .size(Math.min(urlButtonWidths, 200), 20) + .build(); + + // Issues button + this.issuesButton = ButtonWidget.builder(ModMenuScreenTexts.ISSUES, button -> { + final Mod mod = Objects.requireNonNull(selected).getMod(); + boolean isMinecraft = selected.getMod().getId().equals("minecraft"); + if (isMinecraft) { + ConfirmLinkScreen.open(this, Urls.SNAPSHOT_BUGS, true); + } else { + var url = mod.getIssueTracker(); + ConfirmLinkScreen.open(this, url, false); + } + }) + .position(this.rightPaneX + urlButtonWidths + 4 + (urlButtonWidths / 2) - (cappedButtonWidth / 2), RIGHT_PANE_Y + 36) + .size(Math.min(urlButtonWidths, 200), 20) + .build(); + + // Description list + this.descriptionListWidget = new DescriptionListWidget( + this.client, + this.paneWidth, + this.height - RIGHT_PANE_Y - 96, + RIGHT_PANE_Y + 60, + textRenderer.fontHeight + 1, + this.descriptionListWidget, + this + ); + this.descriptionListWidget.setX(this.rightPaneX); + + // Mods folder button + ClickableWidget modsFolderButton = ButtonWidget.builder(ModMenuScreenTexts.MODS_FOLDER, button -> Util.getOperatingSystem().open(FabricLoader.getInstance().getGameDir().resolve("mods").toUri())).position(this.width / 2 - 154, this.height - 28).size(150, 20).build(); + + // Done button + ClickableWidget doneButton = ButtonWidget.builder(ScreenTexts.DONE, button -> client.setScreen(previousScreen)).position(this.width / 2 + 4, this.height - 28).size(150, 20).build(); + + // Initialize data + modList.finalizeInit(); + this.setFilterOptionsShown(this.keepFilterOptionsShown && this.filterOptionsShown); + + // Add children + this.addSelectableChild(this.searchBox); + this.setInitialFocus(this.searchBox); + if (this.filtersButton != null) { + this.addDrawableChild(this.filtersButton); + } + + this.addDrawableChild(this.sortingButton); + this.addDrawableChild(this.librariesButton); + this.addSelectableChild(this.modList); + if (this.configureButton != null) { + this.addDrawableChild(this.configureButton); + } + + this.addDrawableChild(this.websiteButton); + this.addDrawableChild(this.issuesButton); + this.addSelectableChild(this.descriptionListWidget); + this.addDrawableChild(modsFolderButton); + this.addDrawableChild(doneButton); + + this.init = true; + this.keepFilterOptionsShown = true; + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return super.keyPressed(keyCode, scanCode, modifiers) || this.searchBox.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean charTyped(char chr, int keyCode) { + return this.searchBox.charTyped(chr, keyCode); + } + + @Override + public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) { + super.render(drawContext, mouseX, mouseY, delta); + ModListEntry selectedEntry = selected; + if (selectedEntry != null) { + this.descriptionListWidget.render(drawContext, mouseX, mouseY, delta); + } + + this.modList.render(drawContext, mouseX, mouseY, delta); + this.searchBox.render(drawContext, mouseX, mouseY, delta); + drawContext.drawCenteredTextWithShadow(this.textRenderer, this.title, this.modList.getWidth() / 2, 8, 0xFFFFFFFF); + assert client != null; + int grayColor = 0xFFAAAAAA; + if (!ModMenuConfig.DISABLE_DRAG_AND_DROP.getValue()) { + drawContext.drawCenteredTextWithShadow( + this.textRenderer, + ModMenuScreenTexts.DROP_INFO_LINE_1, + this.width - this.modList.getWidth() / 2, + RIGHT_PANE_Y / 2 - client.textRenderer.fontHeight - 1, + grayColor + ); + drawContext.drawCenteredTextWithShadow( + this.textRenderer, + ModMenuScreenTexts.DROP_INFO_LINE_2, + this.width - this.modList.getWidth() / 2, + RIGHT_PANE_Y / 2 + 1, + grayColor + ); + } + + if (!ModMenuConfig.CONFIG_MODE.getValue()) { + Text fullModCount = this.computeModCountText(true, false); + if (!ModMenuConfig.CONFIG_MODE.getValue() && this.updateFiltersX(false)) { + if (this.filterOptionsShown) { + if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || + textRenderer.getWidth(fullModCount) <= this.filtersX - 5) { + drawContext.drawText( + textRenderer, + fullModCount.asOrderedText(), + this.searchBoxX, + 52, + 0xFFFFFFFF, + true + ); + } else { + drawContext.drawText( + textRenderer, + computeModCountText(false, false).asOrderedText(), + this.searchBoxX, + 46, + 0xFFFFFFFF, + true + ); + drawContext.drawText( + textRenderer, + computeLibraryCountText(false).asOrderedText(), + this.searchBoxX, + 57, + 0xFFFFFFFF, + true + ); + } + } else { + if (!ModMenuConfig.SHOW_LIBRARIES.getValue() || + textRenderer.getWidth(fullModCount) <= modList.getWidth() - 5) { + drawContext.drawText(textRenderer, + fullModCount.asOrderedText(), + this.searchBoxX, + 52, + 0xFFFFFFFF, + true + ); + } else { + drawContext.drawText( + textRenderer, + computeModCountText(false, false).asOrderedText(), + this.searchBoxX, + 46, + 0xFFFFFFFF, + true + ); + drawContext.drawText( + textRenderer, + computeLibraryCountText(false).asOrderedText(), + this.searchBoxX, + 57, + 0xFFFFFFFF, + true + ); + } + } + } + } + + if (selectedEntry != null) { + Mod mod = selectedEntry.getMod(); + int x = this.rightPaneX; + if ("java".equals(mod.getId())) { + DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, RIGHT_PANE_Y, 32, 32); + } + + drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, this.selected.getIconTexture(), x, RIGHT_PANE_Y, 0.0F, 0.0F, 32, 32, 32, 32, 0xFFFFFFFF); + int lineSpacing = textRenderer.fontHeight + 1; + int imageOffset = 36; + Text name = Text.literal(mod.getTranslatedName()); + StringVisitable trimmedName = name; + int maxNameWidth = this.width - (x + imageOffset); + if (textRenderer.getWidth(name) > maxNameWidth) { + StringVisitable ellipsis = StringVisitable.plain("..."); + trimmedName = StringVisitable.concat(textRenderer.trimToWidth(name, maxNameWidth - textRenderer.getWidth(ellipsis)), ellipsis); + } + + drawContext.drawText( + textRenderer, + Language.getInstance().reorder(trimmedName), + x + imageOffset, + RIGHT_PANE_Y + 1, + 0xFFFFFFFF, + true + ); + + if (mouseX > x + imageOffset && mouseY > RIGHT_PANE_Y + 1 && + mouseY < RIGHT_PANE_Y + 1 + textRenderer.fontHeight && + mouseX < x + imageOffset + textRenderer.getWidth(trimmedName)) { + drawContext.drawTooltip(ModMenuScreenTexts.modIdTooltip(mod.getId()), mouseX, mouseY); + } + + if (this.init || modBadgeRenderer == null || modBadgeRenderer.getMod() != mod) { + modBadgeRenderer = new ModBadgeRenderer( + x + imageOffset + client.textRenderer.getWidth(trimmedName) + 2, + RIGHT_PANE_Y, + width - 28, + selectedEntry.mod, + this + ); + this.init = false; + } + + if (!ModMenuConfig.HIDE_BADGES.getValue()) { + modBadgeRenderer.draw(drawContext, mouseX, mouseY); + } + + if (mod.isReal()) { + drawContext.drawText( + textRenderer, + mod.getPrefixedVersion(), + x + imageOffset, + RIGHT_PANE_Y + 2 + lineSpacing, + 0xFFAAAAAA, + true + ); + } + + String authors; + List names = mod.getAuthors(); + if (!names.isEmpty()) { + if (names.size() > 1) { + authors = Joiner.on(", ").join(names); + } else { + authors = names.getFirst(); + } + + DrawingUtil.drawWrappedString( + drawContext, + I18n.translate("modmenu.authorPrefix", authors), + x + imageOffset, + RIGHT_PANE_Y + 2 + lineSpacing * 2, + this.paneWidth - imageOffset - 4, + 1, + 0xFFAAAAAA + ); + } + } + } + + private Text computeModCountText(boolean includeLibs, boolean onInit) { + int[] rootMods = formatModCount(ModMenu.ROOT_MODS.values() + .stream() + .filter(mod -> !mod.isHidden() && !mod.getBadges().contains(Mod.Badge.LIBRARY)) + .map(Mod::getId) + .collect(Collectors.toSet()), onInit); + if (includeLibs && ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { + int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() + .stream() + .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) + .map(Mod::getId) + .collect(Collectors.toSet()), false); + return TranslationUtil.translateNumeric("modmenu.showingModsLibraries", rootMods, rootLibs); + } else { + return TranslationUtil.translateNumeric("modmenu.showingMods", rootMods); + } + } + + private Text computeLibraryCountText(boolean onInit) { + if (ModMenuConfig.SHOW_LIBRARIES.getValue() && !onInit) { + int[] rootLibs = formatModCount(ModMenu.ROOT_MODS.values() + .stream() + .filter(mod -> !mod.isHidden() && mod.getBadges().contains(Mod.Badge.LIBRARY)) + .map(Mod::getId) + .collect(Collectors.toSet()), false); + return TranslationUtil.translateNumeric("modmenu.showingLibraries", rootLibs); + } else { + return Text.empty(); + } + } + + 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}; + } + } + + @Override + public void close() { + this.modList.close(); + this.client.setScreen(this.previousScreen); + } + + private void setFilterOptionsShown(boolean filterOptionsShown) { + this.filterOptionsShown = filterOptionsShown; + this.sortingButton.visible = filterOptionsShown; + this.librariesButton.visible = filterOptionsShown; + } + + public ModListEntry getSelectedEntry() { + return selected; + } + + public void updateSelectedEntry(ModListEntry entry) { + if (entry == null) { + return; + } + + this.selected = entry; + String modId = selected.getMod().getId(); + + this.descriptionListWidget.updateSelectedModIfRequired(selected.getMod()); + + if (this.configureButton != null) { + + this.configureButton.active = getModHasConfigScreen(modId); + this.configureButton.visible = + selected != null && getModHasConfigScreen(modId) || modScreenErrors.containsKey(modId); + + if (modScreenErrors.containsKey(modId)) { + Throwable e = modScreenErrors.get(modId); + this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.configureError(modId, e))); + } else { + this.configureButton.setTooltip(Tooltip.of(ModMenuScreenTexts.CONFIGURE)); + } + } + + boolean isMinecraft = modId.equals("minecraft"); + this.websiteButton.setMessage(isMinecraft ? SEND_FEEDBACK_TEXT : ModMenuScreenTexts.WEBSITE); + this.issuesButton.setMessage(isMinecraft ? REPORT_BUGS_TEXT : ModMenuScreenTexts.ISSUES); + + this.websiteButton.visible = true; + this.websiteButton.active = isMinecraft || selected.getMod().getWebsite() != null; + + this.issuesButton.visible = true; + this.issuesButton.active = isMinecraft || selected.getMod().getIssueTracker() != null; + } + + public double getScrollPercent() { + return scrollPercent; + } + + public void updateScrollPercent(double scrollPercent) { + this.scrollPercent = scrollPercent; + } + + public String getSearchInput() { + return this.searchBox.getText(); + } + + private boolean updateFiltersX(boolean onInit) { + if ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(true, onInit)) + 20) >= this.searchRowWidth && + ((this.filtersWidth + textRenderer.getWidth(this.computeModCountText(false, onInit)) + 20) >= this.searchRowWidth || + (this.filtersWidth + textRenderer.getWidth(this.computeLibraryCountText(onInit)) + 20) >= this.searchRowWidth + )) { + this.filtersX = this.paneWidth / 2 - this.filtersWidth / 2; + return !filterOptionsShown; + } else { + this.filtersX = this.searchRowWidth - this.filtersWidth + 1; + return true; + } + } + + @Override + public void onFilesDropped(List paths) { + Path modsDirectory = FabricLoader.getInstance().getGameDir().resolve("mods"); + + // Filter out none mods + List mods = paths.stream().filter(ModsScreen::isValidMod).toList(); + if (mods.isEmpty()) { + return; + } + + String modList = mods.stream().map(Path::getFileName).map(Path::toString).collect(Collectors.joining(", ")); + assert this.client != null; + this.client.setScreen(new ConfirmScreen((value) -> { + if (value) { + boolean allSuccessful = true; + for (Path path : mods) { + try { + Files.copy(path, modsDirectory.resolve(path.getFileName())); + } catch (IOException e) { + LOGGER.warn("Failed to copy mod from {} to {}", path, modsDirectory.resolve(path.getFileName())); + SystemToast.addPackCopyFailure(client, path.toString()); + allSuccessful = false; + break; + } + } + + if (allSuccessful) { + SystemToast.add(client.getToastManager(), SystemToast.Type.PERIODIC_NOTIFICATION, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_1, ModMenuScreenTexts.DROP_SUCCESSFUL_LINE_2); + } + } + this.client.setScreen(this); + }, ModMenuScreenTexts.DROP_CONFIRM, Text.literal(modList))); + } + + private static boolean isValidMod(Path mod) { + try (JarFile jarFile = new JarFile(mod.toFile())) { + var isFabricMod = jarFile.getEntry("fabric.mod.json") != null; + if (!ModMenu.RUNNING_QUILT) { + return isFabricMod; + } else { + return isFabricMod || jarFile.getEntry("quilt.mod.json") != null; + } + } catch (IOException e) { + return false; + } + } + + public boolean getModHasConfigScreen(String modId) { + if (this.modScreenErrors.containsKey(modId)) { + return false; + } else { + return this.modHasConfigScreen.computeIfAbsent(modId, ModMenu::hasConfigScreen); + } + } + + public void safelyOpenConfigScreen(String modId) { + try { + Screen screen = ModMenu.getConfigScreen(modId, this); + if (screen != null) { + assert this.client != null; + this.client.setScreen(screen); + } + } catch (java.lang.NoClassDefFoundError e) { + LOGGER.warn("The '{}' mod config screen is not available because {} is missing.", modId, e.getLocalizedMessage()); + modScreenErrors.put(modId, e); + } catch (Throwable e) { + LOGGER.error("Error from mod '{}'", modId, e); + modScreenErrors.put(modId, e); + } + } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java index 1c7e0ea9..d9cc9a04 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/DescriptionListWidget.java @@ -35,404 +35,423 @@ import java.util.*; public class DescriptionListWidget extends EntryListWidget { - private static final Text HAS_UPDATE_TEXT = Text.translatable("modmenu.hasUpdate"); - private static final Text EXPERIMENTAL_TEXT = Text.translatable("modmenu.experimental").formatted(Formatting.GOLD); - private static final Text DOWNLOAD_TEXT = Text.translatable("modmenu.downloadLink").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); - private static final Text CHILD_HAS_UPDATE_TEXT = Text.translatable("modmenu.childHasUpdate"); - private static final Text LINKS_TEXT = Text.translatable("modmenu.links"); - private static final Text SOURCE_TEXT = Text.translatable("modmenu.source").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); - private static final Text LICENSE_TEXT = Text.translatable("modmenu.license"); - private static final Text VIEW_CREDITS_TEXT = Text.translatable("modmenu.viewCredits").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); - private static final Text CREDITS_TEXT = Text.translatable("modmenu.credits"); - - private final ModsScreen parent; - private final TextRenderer textRenderer; - private Mod selectedMod = null; - - public DescriptionListWidget( - MinecraftClient client, - int width, - int height, - int y, - int itemHeight, - DescriptionListWidget copyFrom, - ModsScreen parent - ) { - super(client, width, height, y, itemHeight); - this.parent = parent; - this.textRenderer = client.textRenderer; - - if (copyFrom != null) { - updateSelectedModIfRequired(copyFrom.selectedMod); - setScrollY(copyFrom.getScrollY()); - } - - if (parent.getSelectedEntry() != null) { - updateSelectedModIfRequired(parent.getSelectedEntry().getMod()); - } - } - - @Override - public DescriptionEntry getSelectedOrNull() { - return null; - } - - @Override - public int getRowWidth() { - return this.width - 10; - } - - @Override - protected int getScrollbarX() { - return this.width - 6 + this.getX(); - } - - @Override - public void appendClickableNarrations(NarrationMessageBuilder builder) { - if (selectedMod != null) { - builder.put( - NarrationPart.TITLE, - selectedMod.getTranslatedName() + " " + selectedMod.getPrefixedVersion()); - } - } - - private void rebuildUI() { - if (selectedMod == null) { - return; - } - - DescriptionEntry emptyEntry = new DescriptionEntry(OrderedText.EMPTY); - int wrapWidth = getRowWidth() - 5; - - Mod mod = selectedMod; - Text description = mod.getFormattedDescription(); - if (!description.getString().isEmpty()) { - for (OrderedText line : textRenderer.wrapLines(description, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - } - - if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() - .contains(mod.getId())) { - UpdateInfo updateInfo = mod.getUpdateInfo(); - if (updateInfo != null && updateInfo.isUpdateAvailable()) { - children().add(emptyEntry); - int index = 0; - for (OrderedText line : textRenderer.wrapLines(HAS_UPDATE_TEXT, wrapWidth - 11)) { - DescriptionEntry entry = new DescriptionEntry(line); - if (index == 0) { - entry.setUpdateTextEntry(); - } - - children().add(entry); - index += 1; - } - - for (OrderedText line : textRenderer.wrapLines(EXPERIMENTAL_TEXT, wrapWidth - 16)) { - children().add(new DescriptionEntry(line, 8)); - } - - - Text updateMessage = updateInfo.getUpdateMessage(); - String downloadLink = updateInfo.getDownloadLink(); - if (updateMessage == null) { - updateMessage = DOWNLOAD_TEXT; - } else { - if (downloadLink != null) { - updateMessage = updateMessage.copy().formatted(Formatting.BLUE, Formatting.UNDERLINE); - } - } - for (OrderedText line : textRenderer.wrapLines(updateMessage, wrapWidth - 16)) { - if (downloadLink != null) { - children().add(new LinkEntry(line, downloadLink, 8)); - } else { - children().add(new DescriptionEntry(line, 8)); - } - } - } - - if (mod.getChildHasUpdate()) { - children().add(emptyEntry); - - int index = 0; - for (OrderedText line : textRenderer.wrapLines(CHILD_HAS_UPDATE_TEXT, wrapWidth - 11)) { - DescriptionEntry entry = new DescriptionEntry(line); - if (index == 0) { - entry.setUpdateTextEntry(); - } - - children().add(entry); - index += 1; - } - } - } - - Map links = mod.getLinks(); - String sourceLink = mod.getSource(); - if ((!links.isEmpty() || sourceLink != null) && !ModMenuConfig.HIDE_MOD_LINKS.getValue()) { - children().add(emptyEntry); - - for (OrderedText line : textRenderer.wrapLines(LINKS_TEXT, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - - if (sourceLink != null) { - int indent = 8; - for (OrderedText line : textRenderer.wrapLines(SOURCE_TEXT, wrapWidth - 16)) { - children().add(new LinkEntry(line, sourceLink, indent)); - indent = 16; - } - } - - links.forEach((key, value) -> { - int indent = 8; - for (OrderedText line : textRenderer.wrapLines(Text.translatable(key).formatted(Formatting.BLUE, Formatting.UNDERLINE), wrapWidth - 16)) { - children().add(new LinkEntry(line, value, indent)); - indent = 16; - } - }); - } - - Set licenses = mod.getLicense(); - if (!ModMenuConfig.HIDE_MOD_LICENSE.getValue() && !licenses.isEmpty()) { - children().add(emptyEntry); - for (OrderedText line : textRenderer.wrapLines(LICENSE_TEXT, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - - for (String license : licenses) { - int indent = 8; - for (OrderedText line : textRenderer.wrapLines(Text.literal(license), wrapWidth - 16)) { - children().add(new DescriptionEntry(line, indent)); - indent = 16; - } - } - } - - if (!ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { - if ("minecraft".equals(mod.getId())) { - children().add(emptyEntry); - for (OrderedText line : textRenderer.wrapLines(VIEW_CREDITS_TEXT, wrapWidth)) { - children().add(new MojangCreditsEntry(line)); - } - } else if (!"java".equals(mod.getId())) { - var credits = mod.getCredits(); - if (!credits.isEmpty()) { - children().add(emptyEntry); - for (OrderedText line : textRenderer.wrapLines(CREDITS_TEXT, wrapWidth)) { - children().add(new DescriptionEntry(line)); - } - - var iterator = credits.entrySet().iterator(); - while (iterator.hasNext()) { - int indent = 8; - var role = iterator.next(); - for (var line : textRenderer.wrapLines(this.creditsRoleText(role.getKey()), wrapWidth - 16)) { - children().add(new DescriptionEntry(line, indent)); - indent = 16; - } - - for (var contributor : role.getValue()) { - indent = 16; - for (var line : textRenderer.wrapLines(Text.literal(contributor), wrapWidth - 24)) { - children().add(new DescriptionEntry(line, indent)); - indent = 24; - } - } - - if (iterator.hasNext()) { - children().add(emptyEntry); - } - } - } - } - } - } - - public void updateSelectedModIfRequired(Mod mod) { - if (mod != selectedMod) { - selectedMod = mod; - clearEntries(); - setScrollY(-Double.MAX_VALUE); - rebuildUI(); - } - } - - @Override - public void renderList(DrawContext drawContext, int mouseX, int mouseY, float delta) { - this.enableScissor(drawContext); - super.renderList(drawContext, mouseX, mouseY, delta); - drawContext.disableScissor(); - RenderPipeline pipeline = RenderPipelines.GUI; - try (BufferAllocator alloc = new BufferAllocator(pipeline.getVertexFormat().getVertexSize() * 4)) { - BufferBuilder bufferBuilder = new BufferBuilder(alloc, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - final int black = 0xFF000000; - bufferBuilder.vertex(this.getX(), (this.getY() + 4), 0.0F).color(0); - bufferBuilder.vertex(this.getRight(), (this.getY() + 4), 0.0F).color(0); - bufferBuilder.vertex(this.getRight(), this.getY(), 0.0F).color(black); - bufferBuilder.vertex(this.getX(), this.getY(), 0.0F).color(black); - bufferBuilder.vertex(this.getX(), this.getBottom(), 0.0F).color(black); - bufferBuilder.vertex(this.getRight(), this.getBottom(), 0.0F).color(black); - bufferBuilder.vertex(this.getRight(), (this.getBottom() - 4), 0.0F).color(0); - bufferBuilder.vertex(this.getX(), (this.getBottom() - 4), 0.0F).color(0); - this.renderScrollBar(bufferBuilder); - try (BuiltBuffer builtBuffer = bufferBuilder.endNullable()) { - if (builtBuffer == null) { - alloc.close(); - return; - } - Framebuffer framebuffer = MinecraftClient.getInstance().getFramebuffer(); - RenderSystem.ShapeIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode()); - VertexFormat.IndexType indexType = autoStorageIndexBuffer.getIndexType(); - GpuBuffer vertexBuffer = RenderSystem.getDevice().createBuffer(() -> "Description List", GpuBuffer.USAGE_COPY_DST, builtBuffer.getBuffer().remaining()); - GpuBuffer indexBuffer = autoStorageIndexBuffer.getIndexBuffer(builtBuffer.getDrawParameters().indexCount()); - RenderSystem.getDevice().createCommandEncoder().writeToBuffer(vertexBuffer.slice(), builtBuffer.getBuffer()); - try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Description List", framebuffer.getColorAttachmentView(), OptionalInt.empty(), framebuffer.getDepthAttachmentView(), OptionalDouble.empty())) { - renderPass.setPipeline(pipeline); - renderPass.setVertexBuffer(0, vertexBuffer); - renderPass.setIndexBuffer(indexBuffer, indexType); - renderPass.drawIndexed(0, 0, builtBuffer.getDrawParameters().indexCount(), 1); - } - } - } - } - - public void renderScrollBar(BufferBuilder bufferBuilder) { - int scrollbarStartX = this.getScrollbarX(); - int scrollbarEndX = scrollbarStartX + 6; - int maxScroll = this.getMaxScrollY(); - if (maxScroll > 0) { - int p = (int) ((float) ((this.getBottom() - this.getY()) * (this.getBottom() - this.getY())) / (float) this.getContentsHeightWithPadding()); - p = MathHelper.clamp(p, 32, this.getBottom() - this.getY() - 8); - int q = (int) this.getScrollY() * (this.getBottom() - this.getY() - p) / maxScroll + this.getY(); - if (q < this.getY()) { - q = this.getY(); - } - - final int black = 0xFF000000; - final int firstColor = ColorHelper.fromFloats(255, 128, 128, 128); - final int lastColor = ColorHelper.fromFloats(255, 192, 192, 192); - bufferBuilder.vertex(scrollbarStartX, this.getBottom(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarEndX, this.getBottom(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarEndX, this.getY(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarStartX, this.getY(), 0.0F).color(black); - bufferBuilder.vertex(scrollbarStartX, q + p, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarEndX, q + p, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarEndX, q, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(firstColor); - bufferBuilder.vertex(scrollbarStartX, q + p - 1, 0.0F).color(lastColor); - bufferBuilder.vertex(scrollbarEndX - 1, q + p - 1, 0.0F).color(lastColor); - bufferBuilder.vertex(scrollbarEndX - 1, q, 0.0F).color(lastColor); - bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(lastColor); - } - } - - private Text creditsRoleText(String roleName) { - // Replace spaces and dashes in role names with underscores if they exist - // Notably Quilted Fabric API does this with FabricMC as "Upstream Owner" - var translationKey = roleName.replaceAll("[ -]", "_").toLowerCase(); - // Add an s to the default untranslated string if it ends in r since this - // Fixes common role names people use in English (e.g. Author -> Authors) - var fallback = roleName.endsWith("r") ? roleName + "s" : roleName; - return Text.translatableWithFallback("modmenu.credits.role." + translationKey, fallback).append(Text.literal(":")); - } - - protected class DescriptionEntry extends ElementListWidget.Entry { - protected OrderedText text; - protected int indent; - public boolean updateTextEntry = false; - - public DescriptionEntry(OrderedText text, int indent) { - this.text = text; - this.indent = indent; - } - - public DescriptionEntry(OrderedText text) { - this(text, 0); - } - - public DescriptionEntry setUpdateTextEntry() { - this.updateTextEntry = true; - return this; - } - - @Override - public void render( - DrawContext drawContext, - int index, - int y, - int x, - int itemWidth, - int itemHeight, - int mouseX, - int mouseY, - boolean isSelected, - float delta - ) { - if (updateTextEntry) { - UpdateAvailableBadge.renderBadge(drawContext, x + indent, y); - x += 11; - } - - drawContext.drawTextWithShadow(textRenderer, text, x + indent, y, 0xFFAAAAAA); - } - - @Override - public List children() { - return Collections.emptyList(); - } - - @Override - public List selectableChildren() { - return Collections.emptyList(); - } - } - - protected class MojangCreditsEntry extends DescriptionEntry { - public MojangCreditsEntry(OrderedText text) { - super(text); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (isMouseOver(mouseX, mouseY)) { - client.setScreen(new MinecraftCredits()); - } - - return super.mouseClicked(mouseX, mouseY, button); - } - - class MinecraftCredits extends CreditsAndAttributionScreen { - public MinecraftCredits() { - super(parent); - } - } - } - - protected class LinkEntry extends DescriptionEntry { - private final String link; - - public LinkEntry(OrderedText text, String link, int indent) { - super(text, indent); - this.link = link; - } - - public LinkEntry(OrderedText text, String link) { - this(text, link, 0); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (isMouseOver(mouseX, mouseY)) { - client.setScreen(new ConfirmLinkScreen((open) -> { - if (open) { - Util.getOperatingSystem().open(link); - } - client.setScreen(parent); - }, link, false)); - } - - return super.mouseClicked(mouseX, mouseY, button); - } - } + private static final Text HAS_UPDATE_TEXT = Text.translatable("modmenu.hasUpdate"); + private static final Text EXPERIMENTAL_TEXT = Text.translatable("modmenu.experimental").formatted(Formatting.GOLD); + private static final Text DOWNLOAD_TEXT = Text.translatable("modmenu.downloadLink").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); + private static final Text CHILD_HAS_UPDATE_TEXT = Text.translatable("modmenu.childHasUpdate"); + private static final Text LINKS_TEXT = Text.translatable("modmenu.links"); + private static final Text SOURCE_TEXT = Text.translatable("modmenu.source").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); + private static final Text LICENSE_TEXT = Text.translatable("modmenu.license"); + private static final Text VIEW_CREDITS_TEXT = Text.translatable("modmenu.viewCredits").formatted(Formatting.BLUE).formatted(Formatting.UNDERLINE); + private static final Text CREDITS_TEXT = Text.translatable("modmenu.credits"); + + private final ModsScreen parent; + private final TextRenderer textRenderer; + private Mod selectedMod = null; + + public DescriptionListWidget( + MinecraftClient client, + int width, + int height, + int y, + int itemHeight, + DescriptionListWidget copyFrom, + ModsScreen parent + ) { + super(client, width, height, y, itemHeight); + this.parent = parent; + this.textRenderer = client.textRenderer; + + if(copyFrom != null) { + updateSelectedModIfRequired(copyFrom.selectedMod); + setScrollY(copyFrom.getScrollY()); + } + + if(parent.getSelectedEntry() != null) { + updateSelectedModIfRequired(parent.getSelectedEntry().getMod()); + } + } + + @Override + public DescriptionEntry getSelectedOrNull() { + return null; + } + + @Override + public int getRowWidth() { + return this.width - 10; + } + + @Override + protected int getScrollbarX() { + return this.width - 6 + this.getX(); + } + + @Override + public void appendClickableNarrations(NarrationMessageBuilder builder) { + if(selectedMod != null) { + builder.put( + NarrationPart.TITLE, + selectedMod.getTranslatedName() + " " + selectedMod.getPrefixedVersion()); + } + } + + private void rebuildUI() { + if (selectedMod == null) { + return; + } + + DescriptionEntry emptyEntry = new DescriptionEntry(OrderedText.EMPTY); + int wrapWidth = getRowWidth() - 5; + + Mod mod = selectedMod; + Text description = mod.getFormattedDescription(); + if (!description.getString().isEmpty()) { + for (OrderedText line : textRenderer.wrapLines(description, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + } + + if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() + .contains(mod.getId())) { + UpdateInfo updateInfo = mod.getUpdateInfo(); + if (updateInfo != null && updateInfo.isUpdateAvailable()) { + children().add(emptyEntry); + + int index = 0; + for (OrderedText line : textRenderer.wrapLines(HAS_UPDATE_TEXT, wrapWidth - 11)) { + DescriptionEntry entry = new DescriptionEntry(line); + if (index == 0) { + entry.setUpdateTextEntry(); + } + + children().add(entry); + index += 1; + } + + for (OrderedText line : textRenderer.wrapLines(EXPERIMENTAL_TEXT, wrapWidth - 16)) { + children().add(new DescriptionEntry(line, 8)); + } + + Text updateMessage = updateInfo.getUpdateMessage(); + String downloadLink = updateInfo.getDownloadLink(); + if (updateMessage == null) { + updateMessage = DOWNLOAD_TEXT; + } else { + if (downloadLink != null) { + updateMessage = updateMessage.copy() + .formatted(Formatting.BLUE) + .formatted(Formatting.UNDERLINE); + } + } + + for (OrderedText line : textRenderer.wrapLines(updateMessage, wrapWidth - 16)) { + if (downloadLink != null) { + children().add(new LinkEntry(line, downloadLink, 8)); + } else { + children().add(new DescriptionEntry(line, 8)); + } + } + } + + if (mod.getChildHasUpdate()) { + children().add(emptyEntry); + + int index = 0; + for (OrderedText line : textRenderer.wrapLines(CHILD_HAS_UPDATE_TEXT, wrapWidth - 11)) { + DescriptionEntry entry = new DescriptionEntry(line); + if (index == 0) { + entry.setUpdateTextEntry(); + } + + children().add(entry); + index += 1; + } + } + } + + Map links = mod.getLinks(); + String sourceLink = mod.getSource(); + if ((!links.isEmpty() || sourceLink != null) && !ModMenuConfig.HIDE_MOD_LINKS.getValue()) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(LINKS_TEXT, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + + if (sourceLink != null) { + int indent = 8; + for (OrderedText line : textRenderer.wrapLines(SOURCE_TEXT, wrapWidth - 16)) { + children().add(new LinkEntry(line, sourceLink, indent)); + indent = 16; + } + } + + links.forEach((key, value) -> { + int indent = 8; + for (OrderedText line : textRenderer.wrapLines(Text.translatable(key) + .formatted(Formatting.BLUE) + .formatted(Formatting.UNDERLINE), + wrapWidth - 16 + )) { + children().add(new LinkEntry(line, value, indent)); + indent = 16; + } + }); + } + + Set licenses = mod.getLicense(); + if (!ModMenuConfig.HIDE_MOD_LICENSE.getValue() && !licenses.isEmpty()) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(LICENSE_TEXT, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + + for (String license : licenses) { + int indent = 8; + for (OrderedText line : textRenderer.wrapLines(Text.literal(license), wrapWidth - 16)) { + children().add(new DescriptionEntry(line, indent)); + indent = 16; + } + } + } + + if (!ModMenuConfig.HIDE_MOD_CREDITS.getValue()) { + if ("minecraft".equals(mod.getId())) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(VIEW_CREDITS_TEXT, wrapWidth)) { + children().add(new MojangCreditsEntry(line)); + } + } else if (!"java".equals(mod.getId())) { + var credits = mod.getCredits(); + + if (!credits.isEmpty()) { + children().add(emptyEntry); + + for (OrderedText line : textRenderer.wrapLines(CREDITS_TEXT, wrapWidth)) { + children().add(new DescriptionEntry(line)); + } + + var iterator = credits.entrySet().iterator(); + + while (iterator.hasNext()) { + int indent = 8; + + var role = iterator.next(); + + + for (var line : textRenderer.wrapLines(this.creditsRoleText(role.getKey()), + wrapWidth - 16 + )) { + children().add(new DescriptionEntry(line, indent)); + indent = 16; + } + + for (var contributor : role.getValue()) { + indent = 16; + + for (var line : textRenderer.wrapLines(Text.literal(contributor), wrapWidth - 24)) { + children().add(new DescriptionEntry(line, indent)); + indent = 24; + } + } + + if (iterator.hasNext()) { + children().add(emptyEntry); + } + } + } + } + } + } + + public void updateSelectedModIfRequired(Mod mod) { + if (mod != selectedMod) { + selectedMod = mod; + clearEntries(); + setScrollY(-Double.MAX_VALUE); + rebuildUI(); + } + } + + @Override + public void renderList(DrawContext drawContext, int mouseX, int mouseY, float delta) { + this.enableScissor(drawContext); + super.renderList(drawContext, mouseX, mouseY, delta); + drawContext.disableScissor(); + + RenderPipeline pipeline = RenderPipelines.GUI; + try (BufferAllocator alloc = new BufferAllocator(pipeline.getVertexFormat().getVertexSize() * 4)) { + BufferBuilder bufferBuilder = new BufferBuilder(alloc, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); + final int black = 0xFF000000; + bufferBuilder.vertex(this.getX(), (this.getY() + 4), 0.0F).color(0); + bufferBuilder.vertex(this.getRight(), (this.getY() + 4), 0.0F).color(0); + bufferBuilder.vertex(this.getRight(), this.getY(), 0.0F).color(black); + bufferBuilder.vertex(this.getX(), this.getY(), 0.0F).color(black); + bufferBuilder.vertex(this.getX(), this.getBottom(), 0.0F).color(black); + bufferBuilder.vertex(this.getRight(), this.getBottom(), 0.0F).color(black); + bufferBuilder.vertex(this.getRight(), (this.getBottom() - 4), 0.0F).color(0); + bufferBuilder.vertex(this.getX(), (this.getBottom() - 4), 0.0F).color(0); + this.renderScrollBar(bufferBuilder); + try (BuiltBuffer builtBuffer = bufferBuilder.endNullable()) { + if (builtBuffer == null) { + alloc.close(); + return; + } + Framebuffer framebuffer = MinecraftClient.getInstance().getFramebuffer(); + RenderSystem.ShapeIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode()); + VertexFormat.IndexType indexType = autoStorageIndexBuffer.getIndexType(); + GpuBuffer vertexBuffer = RenderSystem.getDevice().createBuffer(() -> "Description List", GpuBuffer.USAGE_COPY_DST, builtBuffer.getBuffer().remaining()); + GpuBuffer indexBuffer = autoStorageIndexBuffer.getIndexBuffer(builtBuffer.getDrawParameters().indexCount()); + RenderSystem.getDevice().createCommandEncoder().writeToBuffer(vertexBuffer.slice(), builtBuffer.getBuffer()); + try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Description List", framebuffer.getColorAttachmentView(), OptionalInt.empty(), framebuffer.getDepthAttachmentView(), OptionalDouble.empty())) { + renderPass.setPipeline(pipeline); + renderPass.setVertexBuffer(0, vertexBuffer); + renderPass.setIndexBuffer(indexBuffer, indexType); + renderPass.drawIndexed(0, 0, builtBuffer.getDrawParameters().indexCount(), 1); + } + } + } + } + + public void renderScrollBar(BufferBuilder bufferBuilder) { + int scrollbarStartX = this.getScrollbarX(); + int scrollbarEndX = scrollbarStartX + 6; + int maxScroll = this.getMaxScrollY(); + if (maxScroll > 0) { + int p = (int) ((float) ((this.getBottom() - this.getY()) * (this.getBottom() - this.getY())) / (float) this.getContentsHeightWithPadding()); + p = MathHelper.clamp(p, 32, this.getBottom() - this.getY() - 8); + int q = (int) this.getScrollY() * (this.getBottom() - this.getY() - p) / maxScroll + this.getY(); + if (q < this.getY()) { + q = this.getY(); + } + + final int black = 0xFF000000; + final int firstColor = ColorHelper.fromFloats(255, 128, 128, 128); + final int lastColor = ColorHelper.fromFloats(255, 192, 192, 192); + bufferBuilder.vertex(scrollbarStartX, this.getBottom(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarEndX, this.getBottom(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarEndX, this.getY(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarStartX, this.getY(), 0.0F).color(black); + bufferBuilder.vertex(scrollbarStartX, q + p, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarEndX, q + p, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarEndX, q, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(firstColor); + bufferBuilder.vertex(scrollbarStartX, q + p - 1, 0.0F).color(lastColor); + bufferBuilder.vertex(scrollbarEndX - 1, q + p - 1, 0.0F).color(lastColor); + bufferBuilder.vertex(scrollbarEndX - 1, q, 0.0F).color(lastColor); + bufferBuilder.vertex(scrollbarStartX, q, 0.0F).color(lastColor); + } + } + + private Text creditsRoleText(String roleName) { + // Replace spaces and dashes in role names with underscores if they exist + // Notably Quilted Fabric API does this with FabricMC as "Upstream Owner" + var translationKey = roleName.replaceAll("[ -]", "_").toLowerCase(); + // Add an s to the default untranslated string if it ends in r since this + // Fixes common role names people use in English (e.g. Author -> Authors) + var fallback = roleName.endsWith("r") ? roleName + "s" : roleName; + return Text.translatableWithFallback("modmenu.credits.role." + translationKey, fallback).append(Text.literal(":")); + } + + protected class DescriptionEntry extends ElementListWidget.Entry { + protected OrderedText text; + protected int indent; + public boolean updateTextEntry = false; + + public DescriptionEntry(OrderedText text, int indent) { + this.text = text; + this.indent = indent; + } + + public DescriptionEntry(OrderedText text) { + this(text, 0); + } + + public DescriptionEntry setUpdateTextEntry() { + this.updateTextEntry = true; + return this; + } + + @Override + public void render( + DrawContext drawContext, + int index, + int y, + int x, + int itemWidth, + int itemHeight, + int mouseX, + int mouseY, + boolean isSelected, + float delta + ) { + if (updateTextEntry) { + UpdateAvailableBadge.renderBadge(drawContext, x + indent, y); + x += 11; + } + + drawContext.drawTextWithShadow(textRenderer, text, x + indent, y, 0xFFAAAAAA); + } + + @Override + public List children() { + return Collections.emptyList(); + } + + @Override + public List selectableChildren() { + return Collections.emptyList(); + } + } + + protected class MojangCreditsEntry extends DescriptionEntry { + public MojangCreditsEntry(OrderedText text) { + super(text); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isMouseOver(mouseX, mouseY)) { + client.setScreen(new MinecraftCredits()); + } + + return super.mouseClicked(mouseX, mouseY, button); + } + + class MinecraftCredits extends CreditsAndAttributionScreen { + public MinecraftCredits() { + super(parent); + } + } + } + + protected class LinkEntry extends DescriptionEntry { + private final String link; + + public LinkEntry(OrderedText text, String link, int indent) { + super(text, indent); + this.link = link; + } + + public LinkEntry(OrderedText text, String link) { + this(text, link, 0); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isMouseOver(mouseX, mouseY)) { + client.setScreen(new ConfirmLinkScreen((open) -> { + if (open) { + Util.getOperatingSystem().open(link); + } + client.setScreen(parent); + }, link, false)); + } + + return super.mouseClicked(mouseX, mouseY, button); + } + } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java index 31d61e93..76ed59ae 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java @@ -53,7 +53,7 @@ public void renderWidget(DrawContext context, int mouseX, int mouseY, float delt } context.drawTexture( - RenderPipelines.GUI_TEXTURED, + RenderPipelines.GUI_TEXTURED, this.texture, this.getX(), this.getY(), diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java index 40d7e896..d775b676 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/ModListWidget.java @@ -209,27 +209,26 @@ private void filter(String searchTerm, boolean refresh, boolean search) { @Override protected void renderList(DrawContext drawContext, int mouseX, int mouseY, float delta) { - int entryLeft = this.getRowLeft(); - int entryWidth = this.getRowWidth(); - int entryHeight = this.itemHeight - 4; - int entryCount = this.getEntryCount(); + int entryLeft = this.getRowLeft(); + int entryWidth = this.getRowWidth(); + int entryHeight = this.itemHeight - 4; + int entryCount = this.getEntryCount(); for (int index = 0; index < entryCount; ++index) { int entryTop = this.getRowTop(index) + 2; int entryBottom = this.getRowBottom(index); if (entryBottom >= this.getY() && entryTop <= this.getBottom()) { ModListEntry entry = this.getEntry(index); if (this.isSelectedEntry(index)) { - int entryContentLeft = entryLeft + entry.getXOffset() - 2; - int entryContentWidth = entryWidth - entry.getXOffset() + 4; - this.drawSelectionHighlight( - drawContext, - entryContentLeft, - entryTop, - entryContentWidth, - entryHeight, - this.isFocused() ? Colors.WHITE : Colors.GRAY, - Colors.BLACK - ); + int entryContentLeft = entryLeft + entry.getXOffset() - 2; + int entryContentWidth = entryWidth - entry.getXOffset() + 4; + this.drawSelectionHighlight( + drawContext, + entryContentLeft, + entryTop, + entryContentWidth, + entryHeight, + this.isFocused() ? Colors.WHITE : Colors.GRAY, Colors.BLACK + ); } entry.render( @@ -248,15 +247,15 @@ protected void renderList(DrawContext drawContext, int mouseX, int mouseY, float } } - /** - * Version of {@link #drawSelectionHighlight(DrawContext, int, int, int, int, int)} with unconstrained positioning and sizing. - */ - protected void drawSelectionHighlight(DrawContext context, int x, int y, int width, int height, int borderColor, int fillColor) { - context.fill(x, y - 2, x + width, y + height + 2, borderColor); - context.fill(x + 1, y - 1, x + width - 1, y + height + 1, fillColor); - } + /** + * Version of {@link #drawSelectionHighlight(DrawContext, int, int, int, int, int)} with unconstrained positioning and sizing. + */ + protected void drawSelectionHighlight(DrawContext context, int x, int y, int width, int height, int borderColor, int fillColor) { + context.fill(x, y - 2, x + width, y + height + 2, borderColor); + context.fill(x + 1, y - 1, x + width - 1, y + height + 1, fillColor); + } - public void ensureVisible(ModListEntry entry) { + public void ensureVisible(ModListEntry entry) { super.ensureVisible(entry); } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java index 73c5efbb..73565034 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ModListEntry.java @@ -21,194 +21,195 @@ import net.minecraft.util.math.ColorHelper; public class ModListEntry extends AlwaysSelectedEntryListWidget.Entry { - private static final Identifier MOD_CONFIGURATION_ICON = Identifier.of(ModMenu.MOD_ID, "textures/gui/mod_configuration.png"); - private static final Identifier ERROR_ICON = Identifier.ofVanilla("world_list/error"); - private static final Identifier ERROR_HIGHLIGHTED_ICON = Identifier.ofVanilla("world_list/error_highlighted"); - - protected final MinecraftClient client; - public final Mod mod; - protected final ModListWidget list; - protected Identifier iconLocation; - protected static final int FULL_ICON_SIZE = 32; - protected static final int COMPACT_ICON_SIZE = 19; - protected long sinceLastClick; - - public ModListEntry(Mod mod, ModListWidget list) { - this.mod = mod; - this.list = list; - this.client = MinecraftClient.getInstance(); - } - - @Override - public Text getNarration() { - return Text.literal(mod.getTranslatedName()); - } - - @Override - public void render( - DrawContext drawContext, - int index, - int y, - int x, - int rowWidth, - int rowHeight, - int mouseX, - int mouseY, - boolean hovered, - float delta - ) { - x += getXOffset(); - rowWidth -= getXOffset(); - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - String modId = mod.getId(); - if ("java".equals(modId)) { - DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, y, iconSize, iconSize); - } - - drawContext.drawTexture( - RenderPipelines.GUI_TEXTURED, - this.getIconTexture(), - x, - y, - 0.0F, - 0.0F, - iconSize, - iconSize, - iconSize, - iconSize, - ColorHelper.getWhite(1.0F) - ); - - Text name = Text.literal(mod.getTranslatedName()); - StringVisitable trimmedName = name; - int maxNameWidth = rowWidth - iconSize - 3; - TextRenderer font = this.client.textRenderer; - if (font.getWidth(name) > maxNameWidth) { - StringVisitable ellipsis = StringVisitable.plain("..."); - trimmedName = StringVisitable.concat(font.trimToWidth(name, maxNameWidth - font.getWidth(ellipsis)), ellipsis); - } - - drawContext.drawTextWithShadow( - font, - Language.getInstance().reorder(trimmedName), - x + iconSize + 3, - y + 1, - 0xFFFFFFFF - ); - - var updateBadgeXOffset = 0; - if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue().contains(modId) && (mod.hasUpdate() || mod.getChildHasUpdate())) { - UpdateAvailableBadge.renderBadge(drawContext, x + iconSize + 3 + font.getWidth(name) + 2, y); - updateBadgeXOffset = 11; - } - - if (!ModMenuConfig.HIDE_BADGES.getValue()) { - new ModBadgeRenderer( - x + iconSize + 3 + font.getWidth(name) + 2 + updateBadgeXOffset, - y, - x + rowWidth, - mod, - list.getParent() - ).draw(drawContext, mouseX, mouseY); - } - - if (!ModMenuConfig.COMPACT_LIST.getValue()) { - String summary = mod.getSummary(); - DrawingUtil.drawWrappedString( - drawContext, - summary, - (x + iconSize + 3 + 4), - (y + client.textRenderer.fontHeight + 2), - rowWidth - iconSize - 7, - 2, - 0xFF808080 - ); - } else { - DrawingUtil.drawWrappedString( - drawContext, - mod.getPrefixedVersion(), - (x + iconSize + 3), - (y + client.textRenderer.fontHeight + 2), - rowWidth - iconSize - 7, - 2, - 0xFF808080 - ); - } - - if (!(this instanceof ParentEntry) && ModMenuConfig.QUICK_CONFIGURE.getValue() && (this.list.getParent().getModHasConfigScreen(modId) || this.list.getParent().modScreenErrors.containsKey(modId))) { - final int textureSize = ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; - if (this.client.options.getTouchscreen().getValue() || hovered) { - drawContext.fill(x, y, x + iconSize, y + iconSize, -1601138544); - boolean hoveringIcon = mouseX - x < iconSize; - if (this.list.getParent().modScreenErrors.containsKey(modId)) { - drawContext.drawGuiTexture( - RenderPipelines.GUI_TEXTURED, - hoveringIcon ? ERROR_HIGHLIGHTED_ICON : ERROR_ICON, - x, - y, - iconSize, - iconSize - ); - if (hoveringIcon) { - Throwable e = this.list.getParent().modScreenErrors.get(modId); - // this.list.getParent().setTooltip(this.client.textRenderer.wrapLines(ModMenuScreenTexts.configureError(modId, e), 175)); - } - } else { - int v = hoveringIcon ? iconSize : 0; - drawContext.drawTexture( - RenderPipelines.GUI_TEXTURED, - MOD_CONFIGURATION_ICON, - x, - y, - 0.0F, - (float) v, - iconSize, - iconSize, - textureSize, - textureSize, - ColorHelper.getWhite(1.0F) - ); - } - } - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int delta) { - list.select(this); - if (ModMenuConfig.QUICK_CONFIGURE.getValue() && this.list.getParent().getModHasConfigScreen(this.mod.getId())) { - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - if (mouseX - list.getRowLeft() <= iconSize) { - this.openConfig(); - } else if (Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { - this.openConfig(); - } - } - - this.sinceLastClick = Util.getMeasuringTimeMs(); - return true; - } - - public void openConfig() { - this.list.getParent().safelyOpenConfigScreen(mod.getId()); - } - - public Mod getMod() { - return mod; - } - - public Identifier getIconTexture() { - if (this.iconLocation == null) { - this.iconLocation = Identifier.of(ModMenu.MOD_ID, mod.getId() + "_icon"); - NativeImageBackedTexture icon = mod.getIcon(list.getFabricIconHandler(), 64 * this.client.options.getGuiScale().getValue()); - icon.setFilter(false, false); - this.client.getTextureManager().registerTexture(this.iconLocation, icon); - } - - return iconLocation; - } - - public int getXOffset() { - return 0; - } + public static final Identifier UNKNOWN_ICON = Identifier.ofVanilla("textures/misc/unknown_pack.png"); + private static final Identifier MOD_CONFIGURATION_ICON = Identifier.of(ModMenu.MOD_ID, "textures/gui/mod_configuration.png"); + private static final Identifier ERROR_ICON = Identifier.ofVanilla("world_list/error"); + private static final Identifier ERROR_HIGHLIGHTED_ICON = Identifier.ofVanilla("world_list/error_highlighted"); + + protected final MinecraftClient client; + public final Mod mod; + protected final ModListWidget list; + protected Identifier iconLocation; + protected static final int FULL_ICON_SIZE = 32; + protected static final int COMPACT_ICON_SIZE = 19; + protected long sinceLastClick; + + public ModListEntry(Mod mod, ModListWidget list) { + this.mod = mod; + this.list = list; + this.client = MinecraftClient.getInstance(); + } + + @Override + public Text getNarration() { + return Text.literal(mod.getTranslatedName()); + } + + @Override + public void render( + DrawContext drawContext, + int index, + int y, + int x, + int rowWidth, + int rowHeight, + int mouseX, + int mouseY, + boolean hovered, + float delta + ) { + x += getXOffset(); + rowWidth -= getXOffset(); + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + String modId = mod.getId(); + if ("java".equals(modId)) { + DrawingUtil.drawRandomVersionBackground(mod, drawContext, x, y, iconSize, iconSize); + } + + drawContext.drawTexture( + RenderPipelines.GUI_TEXTURED, + this.getIconTexture(), + x, + y, + 0.0F, + 0.0F, + iconSize, + iconSize, + iconSize, + iconSize, + ColorHelper.getWhite(1.0F) + ); + + Text name = Text.literal(mod.getTranslatedName()); + StringVisitable trimmedName = name; + int maxNameWidth = rowWidth - iconSize - 3; + TextRenderer font = this.client.textRenderer; + if (font.getWidth(name) > maxNameWidth) { + StringVisitable ellipsis = StringVisitable.plain("..."); + trimmedName = StringVisitable.concat(font.trimToWidth(name, maxNameWidth - font.getWidth(ellipsis)), ellipsis); + } + + drawContext.drawTextWithShadow(font, + Language.getInstance().reorder(trimmedName), + x + iconSize + 3, + y + 1, + 0xFFFFFFFF + ); + + var updateBadgeXOffset = 0; + if (ModMenuConfig.UPDATE_CHECKER.getValue() && !ModMenuConfig.DISABLE_UPDATE_CHECKER.getValue() + .contains(modId) && (mod.hasUpdate() || mod.getChildHasUpdate())) { + UpdateAvailableBadge.renderBadge(drawContext, x + iconSize + 3 + font.getWidth(name) + 2, y); + updateBadgeXOffset = 11; + } + + if (!ModMenuConfig.HIDE_BADGES.getValue()) { + new ModBadgeRenderer( + x + iconSize + 3 + font.getWidth(name) + 2 + updateBadgeXOffset, + y, + x + rowWidth, + mod, + list.getParent() + ).draw(drawContext, mouseX, mouseY); + } + + if (!ModMenuConfig.COMPACT_LIST.getValue()) { + String summary = mod.getSummary(); + DrawingUtil.drawWrappedString( + drawContext, + summary, + (x + iconSize + 3 + 4), + (y + client.textRenderer.fontHeight + 2), + rowWidth - iconSize - 7, + 2, + 0xFF808080 + ); + } else { + DrawingUtil.drawWrappedString( + drawContext, + mod.getPrefixedVersion(), + (x + iconSize + 3), + (y + client.textRenderer.fontHeight + 2), + rowWidth - iconSize - 7, + 2, + 0xFF808080 + ); + } + + if (!(this instanceof ParentEntry) && ModMenuConfig.QUICK_CONFIGURE.getValue() && (this.list.getParent().getModHasConfigScreen(modId) || this.list.getParent().modScreenErrors.containsKey(modId))) { + final int textureSize = ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256; + if (this.client.options.getTouchscreen().getValue() || hovered) { + drawContext.fill(x, y, x + iconSize, y + iconSize, -1601138544); + boolean hoveringIcon = mouseX - x < iconSize; + if (this.list.getParent().modScreenErrors.containsKey(modId)) { + drawContext.drawGuiTexture( + RenderPipelines.GUI_TEXTURED, + hoveringIcon ? ERROR_HIGHLIGHTED_ICON : ERROR_ICON, + x, + y, + iconSize, + iconSize + ); + if (hoveringIcon) { + Throwable e = this.list.getParent().modScreenErrors.get(modId); + //this.list.getParent().setTooltip(this.client.textRenderer.wrapLines(ModMenuScreenTexts.configureError(modId, e), 175)); + } + } else { + int v = hoveringIcon ? iconSize : 0; + drawContext.drawTexture( + RenderPipelines.GUI_TEXTURED, + MOD_CONFIGURATION_ICON, + x, + y, + 0.0F, + (float) v, + iconSize, + iconSize, + textureSize, + textureSize, + ColorHelper.getWhite(1.0F) + ); + } + } + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int delta) { + list.select(this); + if (ModMenuConfig.QUICK_CONFIGURE.getValue() && this.list.getParent().getModHasConfigScreen(this.mod.getId())) { + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + if (mouseX - list.getRowLeft() <= iconSize) { + this.openConfig(); + } else if (Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { + this.openConfig(); + } + } + + this.sinceLastClick = Util.getMeasuringTimeMs(); + return true; + } + + public void openConfig() { + this.list.getParent().safelyOpenConfigScreen(mod.getId()); + } + + public Mod getMod() { + return mod; + } + + public Identifier getIconTexture() { + if (this.iconLocation == null) { + this.iconLocation = Identifier.of(ModMenu.MOD_ID, mod.getId() + "_icon"); + NativeImageBackedTexture icon = mod.getIcon(list.getFabricIconHandler(), 64 * this.client.options.getGuiScale().getValue()); + icon.setFilter(false, false); + this.client.getTextureManager().registerTexture(this.iconLocation, icon); + } + + return iconLocation; + } + + public int getXOffset() { + return 0; + } } diff --git a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java index b9151c8a..0dafd7c6 100644 --- a/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java +++ b/src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java @@ -18,188 +18,188 @@ import java.util.Objects; public class ParentEntry extends ModListEntry { - private static final Identifier PARENT_MOD_TEXTURE = Identifier.of(ModMenu.MOD_ID, "textures/gui/parent_mod.png"); - protected List children; - protected ModListWidget list; - protected boolean hoveringIcon = false; - - public ParentEntry(Mod parent, List children, ModListWidget list) { - super(parent, list); - this.children = children; - this.list = list; - } - - @Override - public void render( - DrawContext drawContext, - int index, - int y, - int x, - int rowWidth, - int rowHeight, - int mouseX, - int mouseY, - boolean isSelected, - float delta - ) { - super.render(drawContext, index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); - TextRenderer font = client.textRenderer; - int childrenBadgeHeight = font.fontHeight; - int childrenBadgeWidth = font.fontHeight; - int shownChildren = ModSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); - Text str = shownChildren == children.size() ? - Text.literal(String.valueOf(shownChildren)) : - Text.literal(shownChildren + "/" + children.size()); - int childrenWidth = font.getWidth(str) - 1; - if (childrenBadgeWidth < childrenWidth + 4) { - childrenBadgeWidth = childrenWidth + 4; - } - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - int childrenBadgeX = x + iconSize - childrenBadgeWidth; - int childrenBadgeY = y + iconSize - childrenBadgeHeight; - int childrenOutlineColor = 0xFF107454; - int childrenFillColor = 0xFF093929; - drawContext.fill( - childrenBadgeX + 1, - childrenBadgeY, - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + 1, - childrenOutlineColor - ); - drawContext.fill( - childrenBadgeX, - childrenBadgeY + 1, - childrenBadgeX + 1, - childrenBadgeY + childrenBadgeHeight - 1, - childrenOutlineColor - ); - drawContext.fill( - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + 1, - childrenBadgeX + childrenBadgeWidth, - childrenBadgeY + childrenBadgeHeight - 1, - childrenOutlineColor - ); - drawContext.fill( - childrenBadgeX + 1, - childrenBadgeY + 1, - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + childrenBadgeHeight - 1, - childrenFillColor - ); - drawContext.fill( - childrenBadgeX + 1, - childrenBadgeY + childrenBadgeHeight - 1, - childrenBadgeX + childrenBadgeWidth - 1, - childrenBadgeY + childrenBadgeHeight, - childrenOutlineColor - ); - drawContext.drawText( - font, - str.asOrderedText(), - (int) (childrenBadgeX + (float) childrenBadgeWidth / 2 - (float) childrenWidth / 2), - childrenBadgeY + 1, - 0xFFCACACA, - false - ); - - this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; - if (isMouseOver(mouseX, mouseY)) { - drawContext.fill(x, y, x + iconSize, y + iconSize, 0xA0909090); - int xOffset = list.getParent().showModChildren.contains(getMod().getId()) ? iconSize : 0; - int yOffset = hoveringIcon ? iconSize : 0; - drawContext.drawTexture( - RenderPipelines.GUI_TEXTURED, - PARENT_MOD_TEXTURE, - x, - y, - xOffset, - yOffset, - iconSize + xOffset, - iconSize + yOffset, - ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, - ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, - 0xFFFFFFFF - ); - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int i) { - int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; - boolean quickConfigure = ModMenuConfig.QUICK_CONFIGURE.getValue(); - if (mouseX - list.getRowLeft() <= iconSize) { - this.toggleChildren(); - return true; - } else if (!quickConfigure && Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { - this.toggleChildren(); - return true; - } else { - return super.mouseClicked(mouseX, mouseY, i); - } - } - - private void toggleChildren() { - String id = getMod().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 keyCode, int scanCode, int modifiers) { - String modId = getMod().getId(); - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE) { - if (list.getParent().showModChildren.contains(modId)) { - list.getParent().showModChildren.remove(modId); - } else { - list.getParent().showModChildren.add(modId); - } - - list.filter(list.getParent().getSearchInput(), false); - return true; - } else if (keyCode == GLFW.GLFW_KEY_LEFT) { - if (list.getParent().showModChildren.contains(modId)) { - list.getParent().showModChildren.remove(modId); - list.filter(list.getParent().getSearchInput(), false); - } - - return true; - } else if (keyCode == GLFW.GLFW_KEY_RIGHT) { - if (!list.getParent().showModChildren.contains(modId)) { - list.getParent().showModChildren.add(modId); - list.filter(list.getParent().getSearchInput(), false); - return true; - } else { - return list.keyPressed(GLFW.GLFW_KEY_DOWN, 0, 0); - } - } - - return super.keyPressed(keyCode, scanCode, modifiers); - } - - public void setChildren(List children) { - this.children = children; - } - - public void addChildren(List children) { - this.children.addAll(children); - } - - public void addChildren(Mod... children) { - this.children.addAll(Arrays.asList(children)); - } - - public List getChildren() { - return children; - } - - @Override - public boolean isMouseOver(double double_1, double double_2) { - return Objects.equals(this.list.getEntryAtPos(double_1, double_2), this); - } + private static final Identifier PARENT_MOD_TEXTURE = Identifier.of(ModMenu.MOD_ID, "textures/gui/parent_mod.png"); + protected List children; + protected ModListWidget list; + protected boolean hoveringIcon = false; + + public ParentEntry(Mod parent, List children, ModListWidget list) { + super(parent, list); + this.children = children; + this.list = list; + } + + @Override + public void render( + DrawContext drawContext, + int index, + int y, + int x, + int rowWidth, + int rowHeight, + int mouseX, + int mouseY, + boolean isSelected, + float delta + ) { + super.render(drawContext, index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); + TextRenderer font = client.textRenderer; + int childrenBadgeHeight = font.fontHeight; + int childrenBadgeWidth = font.fontHeight; + int shownChildren = ModSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); + Text str = shownChildren == children.size() ? + Text.literal(String.valueOf(shownChildren)) : + Text.literal(shownChildren + "/" + children.size()); + int childrenWidth = font.getWidth(str) - 1; + if (childrenBadgeWidth < childrenWidth + 4) { + childrenBadgeWidth = childrenWidth + 4; + } + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + int childrenBadgeX = x + iconSize - childrenBadgeWidth; + int childrenBadgeY = y + iconSize - childrenBadgeHeight; + int childrenOutlineColor = 0xff107454; + int childrenFillColor = 0xff093929; + drawContext.fill( + childrenBadgeX + 1, + childrenBadgeY, + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + 1, + childrenOutlineColor + ); + drawContext.fill( + childrenBadgeX, + childrenBadgeY + 1, + childrenBadgeX + 1, + childrenBadgeY + childrenBadgeHeight - 1, + childrenOutlineColor + ); + drawContext.fill( + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + 1, + childrenBadgeX + childrenBadgeWidth, + childrenBadgeY + childrenBadgeHeight - 1, + childrenOutlineColor + ); + drawContext.fill( + childrenBadgeX + 1, + childrenBadgeY + 1, + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + childrenBadgeHeight - 1, + childrenFillColor + ); + drawContext.fill( + childrenBadgeX + 1, + childrenBadgeY + childrenBadgeHeight - 1, + childrenBadgeX + childrenBadgeWidth - 1, + childrenBadgeY + childrenBadgeHeight, + childrenOutlineColor + ); + drawContext.drawText( + font, + str.asOrderedText(), + (int) (childrenBadgeX + (float) childrenBadgeWidth / 2 - (float) childrenWidth / 2), + childrenBadgeY + 1, + 0xFFCACACA, + false + ); + + this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; + if (isMouseOver(mouseX, mouseY)) { + drawContext.fill(x, y, x + iconSize, y + iconSize, 0xA0909090); + int xOffset = list.getParent().showModChildren.contains(getMod().getId()) ? iconSize : 0; + int yOffset = hoveringIcon ? iconSize : 0; + drawContext.drawTexture( + RenderPipelines.GUI_TEXTURED, + PARENT_MOD_TEXTURE, + x, + y, + xOffset, + yOffset, + iconSize + xOffset, + iconSize + yOffset, + ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, + ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, + 0xFFFFFFFF + ); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int i) { + int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; + boolean quickConfigure = ModMenuConfig.QUICK_CONFIGURE.getValue(); + if (mouseX - list.getRowLeft() <= iconSize) { + this.toggleChildren(); + return true; + } else if (!quickConfigure && Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { + this.toggleChildren(); + return true; + } else { + return super.mouseClicked(mouseX, mouseY, i); + } + } + + private void toggleChildren() { + String id = getMod().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 keyCode, int scanCode, int modifiers) { + String modId = getMod().getId(); + if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE) { + if (list.getParent().showModChildren.contains(modId)) { + list.getParent().showModChildren.remove(modId); + } else { + list.getParent().showModChildren.add(modId); + } + + list.filter(list.getParent().getSearchInput(), false); + return true; + } else if (keyCode == GLFW.GLFW_KEY_LEFT) { + if (list.getParent().showModChildren.contains(modId)) { + list.getParent().showModChildren.remove(modId); + list.filter(list.getParent().getSearchInput(), false); + } + + return true; + } else if (keyCode == GLFW.GLFW_KEY_RIGHT) { + if (!list.getParent().showModChildren.contains(modId)) { + list.getParent().showModChildren.add(modId); + list.filter(list.getParent().getSearchInput(), false); + return true; + } else { + return list.keyPressed(GLFW.GLFW_KEY_DOWN, 0, 0); + } + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChildren(List children) { + this.children.addAll(children); + } + + public void addChildren(Mod... children) { + this.children.addAll(Arrays.asList(children)); + } + + public List getChildren() { + return children; + } + + @Override + public boolean isMouseOver(double double_1, double double_2) { + return Objects.equals(this.list.getEntryAtPos(double_1, double_2), this); + } } diff --git a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java index 4306020d..c864c188 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java +++ b/src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java @@ -18,76 +18,87 @@ @Environment(EnvType.CLIENT) public class DrawingUtil { - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - public static void drawRandomVersionBackground( - Mod mod, - DrawContext drawContext, - int x, - int y, - int width, - int height - ) { - int seed = mod.getName().hashCode() + mod.getVersion().hashCode(); + public static void drawRandomVersionBackground( + Mod mod, + DrawContext drawContext, + int x, + int y, + int width, + int height + ) { + int seed = mod.getName().hashCode() + mod.getVersion().hashCode(); - Random random = new Random(seed); - int color = 0xFF000000 | MathHelper.hsvToRgb(random.nextFloat(1f), random.nextFloat(0.7f, 0.8f), 0.9f); - if (!ModMenuConfig.RANDOM_JAVA_COLORS.getValue()) { - color = 0xFFDD5656; - } + Random random = new Random(seed); + int color = 0xFF000000 | MathHelper.hsvToRgb(random.nextFloat(1f), random.nextFloat(0.7f, 0.8f), 0.9f); + if (!ModMenuConfig.RANDOM_JAVA_COLORS.getValue()) { + color = 0xFFDD5656; + } - drawContext.fill(x, y, x + width, y + height, color); - } + drawContext.fill(x, y, x + width, y + height, color); + } - public static void drawWrappedString( - DrawContext drawContext, - String string, - int x, - int y, - int wrapWidth, - int lines, - int color - ) { - while (string != null && string.endsWith("\n")) { - string = string.substring(0, string.length() - 1); - } + public static void drawWrappedString( + DrawContext drawContext, + String string, + int x, + int y, + int wrapWidth, + int lines, + int color + ) { + while (string != null && string.endsWith("\n")) { + string = string.substring(0, string.length() - 1); + } - List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); - for (int i = 0; i < strings.size(); i++) { - if (i >= lines) { - break; - } + List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); + for (int i = 0; i < strings.size(); i++) { + if (i >= lines) { + break; + } - StringVisitable renderable = strings.get(i); - if (i == lines - 1 && strings.size() > lines) { - renderable = StringVisitable.concat(strings.get(i), StringVisitable.plain("...")); - } + StringVisitable renderable = strings.get(i); + if (i == lines - 1 && strings.size() > lines) { + renderable = StringVisitable.concat(strings.get(i), StringVisitable.plain("...")); + } - OrderedText line = Language.getInstance().reorder(renderable); - int x1 = x; - if (CLIENT.textRenderer.isRightToLeft()) { - x1 += wrapWidth - CLIENT.textRenderer.getWidth(line); - } + OrderedText line = Language.getInstance().reorder(renderable); + int x1 = x; + if (CLIENT.textRenderer.isRightToLeft()) { + x1 += wrapWidth - CLIENT.textRenderer.getWidth(line); + } - drawContext.drawTextWithShadow(CLIENT.textRenderer, line, x1, y + i * CLIENT.textRenderer.fontHeight, color); - } - } + drawContext.drawTextWithShadow(CLIENT.textRenderer, line, x1, y + i * CLIENT.textRenderer.fontHeight, color); + } + } - public static void drawBadge( - DrawContext drawContext, - int x, - int y, - int tagWidth, - OrderedText text, - int outlineColor, - int fillColor, - int textColor - ) { - drawContext.fill(x + 1, y - 1, x + tagWidth, y, outlineColor); - drawContext.fill(x, y, x + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); - drawContext.fill(x + 1, y + 1 + CLIENT.textRenderer.fontHeight - 1, x + tagWidth, y + CLIENT.textRenderer.fontHeight + 1, outlineColor); - drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); - drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); - drawContext.drawText(CLIENT.textRenderer, text, (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), y + 1, textColor, false); - } + public static void drawBadge( + DrawContext drawContext, + int x, + int y, + int tagWidth, + OrderedText text, + int outlineColor, + int fillColor, + int textColor + ) { + drawContext.fill(x + 1, y - 1, x + tagWidth, y, outlineColor); + drawContext.fill(x, y, x + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); + drawContext.fill(x + 1, + y + 1 + CLIENT.textRenderer.fontHeight - 1, + x + tagWidth, + y + CLIENT.textRenderer.fontHeight + 1, + outlineColor + ); + drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); + drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); + drawContext.drawText(CLIENT.textRenderer, + text, + (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), + y + 1, + textColor, + false + ); + } } diff --git a/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java b/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java index 19ae2588..2c427c67 100644 --- a/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java +++ b/src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java @@ -9,50 +9,54 @@ import java.util.Set; public class ModBadgeRenderer { - protected int startX, startY, badgeX, badgeY, badgeMax; - protected Mod mod; - protected MinecraftClient client; - protected final ModsScreen screen; - - public ModBadgeRenderer(int startX, int startY, int endX, Mod mod, ModsScreen screen) { - this.startX = startX; - this.startY = startY; - this.badgeMax = endX; - this.mod = mod; - this.screen = screen; - this.client = MinecraftClient.getInstance(); - } - - public void draw(DrawContext drawContext, int mouseX, int mouseY) { - this.badgeX = startX; - this.badgeY = startY; - Set badges = mod.getBadges(); - badges.forEach(badge -> drawBadge(drawContext, badge, mouseX, mouseY)); - } - - public void drawBadge(DrawContext drawContext, Mod.Badge badge, int mouseX, int mouseY) { - this.drawBadge( - drawContext, - badge.getText().asOrderedText(), - badge.getOutlineColor(), - badge.getFillColor() - ); - } - - public void drawBadge( - DrawContext drawContext, - OrderedText text, - int outlineColor, - int fillColor - ) { - int width = client.textRenderer.getWidth(text) + 6; - if (badgeX + width < badgeMax) { - DrawingUtil.drawBadge(drawContext, badgeX, badgeY, width, text, outlineColor, fillColor, 0xFFCACACA); - badgeX += width + 3; - } - } - - public Mod getMod() { - return mod; - } + protected int startX, startY, badgeX, badgeY, badgeMax; + protected Mod mod; + protected MinecraftClient client; + protected final ModsScreen screen; + + public ModBadgeRenderer(int startX, int startY, int endX, Mod mod, ModsScreen screen) { + this.startX = startX; + this.startY = startY; + this.badgeMax = endX; + this.mod = mod; + this.screen = screen; + this.client = MinecraftClient.getInstance(); + } + + public void draw(DrawContext drawContext, int mouseX, int mouseY) { + this.badgeX = startX; + this.badgeY = startY; + Set badges = mod.getBadges(); + badges.forEach(badge -> drawBadge(drawContext, badge, mouseX, mouseY)); + } + + public void drawBadge(DrawContext drawContext, Mod.Badge badge, int mouseX, int mouseY) { + this.drawBadge( + drawContext, + badge.getText().asOrderedText(), + badge.getOutlineColor(), + badge.getFillColor(), + mouseX, + mouseY + ); + } + + public void drawBadge( + DrawContext drawContext, + OrderedText text, + int outlineColor, + int fillColor, + int mouseX, + int mouseY + ) { + int width = client.textRenderer.getWidth(text) + 6; + if (badgeX + width < badgeMax) { + DrawingUtil.drawBadge(drawContext, badgeX, badgeY, width, text, outlineColor, fillColor, 0xFFCACACA); + badgeX += width + 3; + } + } + + public Mod getMod() { + return mod; + } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 5c85a05c..e6d6d714 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,112 +1,112 @@ { - "schemaVersion": 1, - "id": "modmenu", - "name": "Mod Menu", - "version": "$version", - "environment": "client", - "license": "MIT", - "icon": "assets/modmenu/icon.png", - "entrypoints": { - "client": [ - "com.terraformersmc.modmenu.ModMenu" - ], - "modmenu": [ - "com.terraformersmc.modmenu.ModMenuModMenuCompat" - ] - }, - "contact": { - "homepage": "https://modrinth.com/mod/modmenu", - "sources": "https://github.com/TerraformersMC/ModMenu", - "issues": "https://github.com/TerraformersMC/ModMenu/issues" - }, - "depends": { - "fabric-resource-loader-v0": "*", - "fabric-screen-api-v1": "*", - "fabric-key-binding-api-v1": "*", - "fabric-lifecycle-events-v1": "*", - "fabricloader": ">=0.16.10", - "minecraft": "~1.21.6-beta.3" - }, - "authors": [ - "Prospector", - "haykam821", - "TerraformersMC" - ], - "contributors": [ - "shedaniel", - "Draylar", - "Mordna", - "Madis0", - "Yanis48", - "LemmaEOF", - "fewizz", - "XuyuEre", - "geniiii", - "thiagokenis", - "Juuxel", - "Chloe Dawn", - "modmuss50", - "Patbox", - "Lykrast", - "Pyrofab", - "UpcraftLP", - "swordglowsblue", - "hinataaki", - "LeonXu98", - "magneticflux", - "vanja-san", - "AlexIIL", - "masoncook16", - "el97", - "Hephaestus-Dev", - "zabi94", - "dexman545", - "dhouck", - "Hambaka", - "po-stulate", - "FlashyReese", - "egeesin", - "TheGlitch76", - "ludg1e", - "williambl", - "PepperCode1", - "ThatTrollzer", - "ToffeeMax", - "kalucky0", - "Maaster", - "MartrixX", - "NotSteven", - "Dolphin 2.1", - "ENDERZOMBI102", - "anatom3000", - "OroArmor", - "jackassmc", - "Vaerian", - "RDKRACZ", - "Hulenkius", - "XfedeX", - "spnda", - "Jab125", - "SolidBlock", - "Tkain", - "nfitzen", - "DenaryDev", - "legenden", - "NaiNonTH", - "MagnusHJensen", - "Benjamin-Norton", - "triphora", - "Pyrrha" - ], - "description": "Adds a mod menu to view the list of mods you have installed.", - "mixins": [ - "mixins.modmenu.json" - ], - "custom": { - "modmenu": { - "links": { - "modmenu.discord": "https://discord.gg/jEGF5fb" - } - } - } + "schemaVersion": 1, + "id": "modmenu", + "name": "Mod Menu", + "version": "$version", + "environment": "client", + "license": "MIT", + "icon": "assets/modmenu/icon.png", + "entrypoints": { + "client": [ + "com.terraformersmc.modmenu.ModMenu" + ], + "modmenu": [ + "com.terraformersmc.modmenu.ModMenuModMenuCompat" + ] + }, + "contact": { + "homepage": "https://modrinth.com/mod/modmenu", + "sources": "https://github.com/TerraformersMC/ModMenu", + "issues": "https://github.com/TerraformersMC/ModMenu/issues" + }, + "depends": { + "fabric-resource-loader-v0": "*", + "fabric-screen-api-v1": "*", + "fabric-key-binding-api-v1": "*", + "fabric-lifecycle-events-v1": "*", + "fabricloader": ">=0.16.10", + "minecraft": "~1.21.6-rc.1" + }, + "authors": [ + "Prospector", + "haykam821", + "TerraformersMC" + ], + "contributors": [ + "shedaniel", + "Draylar", + "Mordna", + "Madis0", + "Yanis48", + "LemmaEOF", + "fewizz", + "XuyuEre", + "geniiii", + "thiagokenis", + "Juuxel", + "Chloe Dawn", + "modmuss50", + "Patbox", + "Lykrast", + "Pyrofab", + "UpcraftLP", + "swordglowsblue", + "hinataaki", + "LeonXu98", + "magneticflux", + "vanja-san", + "AlexIIL", + "masoncook16", + "el97", + "Hephaestus-Dev", + "zabi94", + "dexman545", + "dhouck", + "Hambaka", + "po-stulate", + "FlashyReese", + "egeesin", + "TheGlitch76", + "ludg1e", + "williambl", + "PepperCode1", + "ThatTrollzer", + "ToffeeMax", + "kalucky0", + "Maaster", + "MartrixX", + "NotSteven", + "Dolphin 2.1", + "ENDERZOMBI102", + "anatom3000", + "OroArmor", + "jackassmc", + "Vaerian", + "RDKRACZ", + "Hulenkius", + "XfedeX", + "spnda", + "Jab125", + "SolidBlock", + "Tkain", + "nfitzen", + "DenaryDev", + "legenden", + "NaiNonTH", + "MagnusHJensen", + "Benjamin-Norton", + "triphora", + "Pyrrha" + ], + "description": "Adds a mod menu to view the list of mods you have installed.", + "mixins": [ + "mixins.modmenu.json" + ], + "custom": { + "modmenu": { + "links": { + "modmenu.discord": "https://discord.gg/jEGF5fb" + } + } + } }