From 0a7f5ff9fa5012ac1a21b1f1efc273cb024d4465 Mon Sep 17 00:00:00 2001 From: tterrag1098 Date: Wed, 10 Jul 2019 20:09:56 -0700 Subject: [PATCH] Rework GuiModList mod info to use custom scrolling panel impl --- .../client/gui/ScrollPanel.java | 270 ++++++++++++++++++ .../fml/client/gui/GuiModList.java | 246 ++++++++-------- 2 files changed, 392 insertions(+), 124 deletions(-) create mode 100644 src/main/java/net/minecraftforge/client/gui/ScrollPanel.java diff --git a/src/main/java/net/minecraftforge/client/gui/ScrollPanel.java b/src/main/java/net/minecraftforge/client/gui/ScrollPanel.java new file mode 100644 index 0000000000..df52d5ac89 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/gui/ScrollPanel.java @@ -0,0 +1,270 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2019. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.gui; + +import java.util.Collections; +import java.util.List; + +import org.lwjgl.opengl.GL11; + +import com.mojang.blaze3d.platform.GlStateManager; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.AbstractGui; +import net.minecraft.client.gui.FocusableGui; +import net.minecraft.client.gui.IGuiEventListener; +import net.minecraft.client.gui.IRenderable; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraftforge.fml.client.config.GuiUtils; + +public abstract class ScrollPanel extends FocusableGui implements IRenderable +{ + private final Minecraft client; + protected final int width; + protected final int height; + protected final int top; + protected final int bottom; + protected final int right; + protected final int left; + private boolean scrolling; + protected float scrollDistance; + protected boolean captureMouse = true; + protected final int border = 4; + + private final int barWidth = 6; + private final int barLeft; + + public ScrollPanel(Minecraft client, int width, int height, int top, int left) + { + this.client = client; + this.width = width; + this.height = height; + this.top = top; + this.left = left; + this.bottom = height + this.top; + this.right = width + this.left; + this.barLeft = this.left + this.width - barWidth; + } + + protected abstract int getContentHeight(); + + protected void drawBackground() {} + + /** + * Draw anything special on the screen. GL_SCISSOR is enabled for anything that + * is rendered outside of the view box. Do not mess with SCISSOR unless you support this. + * @param mouseY + * @param mouseX + */ + protected abstract void drawPanel(int entryRight, int relativeY, Tessellator tess, int mouseX, int mouseY); + + protected boolean clickPanel(double mouseX, double mouseY, int button) { return false; } + + private int getMaxScroll() + { + return this.getContentHeight() - (this.height - this.border); + } + + private void applyScrollLimits() + { + int max = getMaxScroll(); + + if (max < 0) + { + max /= 2; + } + + if (this.scrollDistance < 0.0F) + { + this.scrollDistance = 0.0F; + } + + if (this.scrollDistance > max) + { + this.scrollDistance = max; + } + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double scroll) + { + if (scroll != 0) + { + this.scrollDistance += -scroll * getScrollAmount(); + applyScrollLimits(); + return true; + } + return false; + } + + protected int getScrollAmount() + { + return 20; + } + + @Override + public boolean isMouseOver(double mouseX, double mouseY) + { + return mouseX >= this.left && mouseX <= this.left + this.width && + mouseY >= this.top && mouseY <= this.bottom; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (super.mouseClicked(mouseX, mouseY, button)) + return true; + + this.scrolling = button == 0 && mouseX >= barLeft && mouseX < barLeft + barWidth; + if (this.scrolling) + { + return true; + } + int mouseListY = ((int)mouseY) - this.top - this.getContentHeight() + (int)this.scrollDistance - border; + if (mouseX >= left && mouseX <= right && mouseListY < 0) + { + return this.clickPanel(mouseX - left, mouseY - this.top + (int)this.scrollDistance - border, button); + } + return false; + } + + @Override + public boolean mouseReleased(double p_mouseReleased_1_, double p_mouseReleased_3_, int p_mouseReleased_5_) { + if (super.mouseReleased(p_mouseReleased_1_, p_mouseReleased_3_, p_mouseReleased_5_)) + return true; + boolean ret = this.scrolling; + this.scrolling = false; + return ret; + } + + private int getBarHeight() + { + int barHeight = (height * height) / this.getContentHeight(); + + if (barHeight < 32) barHeight = 32; + + if (barHeight > height - border*2) + barHeight = height - border*2; + + return barHeight; + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) + { + if (this.scrolling) + { + int maxScroll = height - getBarHeight(); + double moved = deltaY / maxScroll; + this.scrollDistance += getMaxScroll() * moved; + applyScrollLimits(); + return true; + } + return false; + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) + { + this.drawBackground(); + + Tessellator tess = Tessellator.getInstance(); + BufferBuilder worldr = tess.getBuffer(); + + double scale = client.mainWindow.getGuiScaleFactor(); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor((int)(left * scale), (int)(client.mainWindow.getHeight() - (bottom * scale)), + (int)(width * scale), (int)(height * scale)); + + if (this.client.world != null) + { + this.drawGradientRect(this.left, this.top, this.right, this.bottom, 0xC0101010, 0xD0101010); + } + else // Draw dark dirt background + { + GlStateManager.disableLighting(); + GlStateManager.disableFog(); + this.client.getTextureManager().bindTexture(AbstractGui.BACKGROUND_LOCATION); + GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); + final float texScale = 32.0F; + worldr.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); + worldr.pos(this.left, this.bottom, 0.0D).tex(this.left / texScale, (this.bottom + (int)this.scrollDistance) / texScale).color(0x20, 0x20, 0x20, 0xFF).endVertex(); + worldr.pos(this.right, this.bottom, 0.0D).tex(this.right / texScale, (this.bottom + (int)this.scrollDistance) / texScale).color(0x20, 0x20, 0x20, 0xFF).endVertex(); + worldr.pos(this.right, this.top, 0.0D).tex(this.right / texScale, (this.top + (int)this.scrollDistance) / texScale).color(0x20, 0x20, 0x20, 0xFF).endVertex(); + worldr.pos(this.left, this.top, 0.0D).tex(this.left / texScale, (this.top + (int)this.scrollDistance) / texScale).color(0x20, 0x20, 0x20, 0xFF).endVertex(); + tess.draw(); + } + + int baseY = this.top + border - (int)this.scrollDistance; + this.drawPanel(right, baseY, tess, mouseX, mouseY); + + GlStateManager.disableDepthTest(); + + int extraHeight = (this.getContentHeight() + border) - height; + if (extraHeight > 0) + { + int barHeight = getBarHeight(); + + int barTop = (int)this.scrollDistance * (height - barHeight) / extraHeight + this.top; + if (barTop < this.top) + { + barTop = this.top; + } + + GlStateManager.disableTexture(); + worldr.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); + worldr.pos(barLeft, this.bottom, 0.0D).tex(0.0D, 1.0D).color(0x00, 0x00, 0x00, 0xFF).endVertex(); + worldr.pos(barLeft + barWidth, this.bottom, 0.0D).tex(1.0D, 1.0D).color(0x00, 0x00, 0x00, 0xFF).endVertex(); + worldr.pos(barLeft + barWidth, this.top, 0.0D).tex(1.0D, 0.0D).color(0x00, 0x00, 0x00, 0xFF).endVertex(); + worldr.pos(barLeft, this.top, 0.0D).tex(0.0D, 0.0D).color(0x00, 0x00, 0x00, 0xFF).endVertex(); + tess.draw(); + worldr.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); + worldr.pos(barLeft, barTop + barHeight, 0.0D).tex(0.0D, 1.0D).color(0x80, 0x80, 0x80, 0xFF).endVertex(); + worldr.pos(barLeft + barWidth, barTop + barHeight, 0.0D).tex(1.0D, 1.0D).color(0x80, 0x80, 0x80, 0xFF).endVertex(); + worldr.pos(barLeft + barWidth, barTop, 0.0D).tex(1.0D, 0.0D).color(0x80, 0x80, 0x80, 0xFF).endVertex(); + worldr.pos(barLeft, barTop, 0.0D).tex(0.0D, 0.0D).color(0x80, 0x80, 0x80, 0xFF).endVertex(); + tess.draw(); + worldr.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); + worldr.pos(barLeft, barTop + barHeight - 1, 0.0D).tex(0.0D, 1.0D).color(0xC0, 0xC0, 0xC0, 0xFF).endVertex(); + worldr.pos(barLeft + barWidth - 1, barTop + barHeight - 1, 0.0D).tex(1.0D, 1.0D).color(0xC0, 0xC0, 0xC0, 0xFF).endVertex(); + worldr.pos(barLeft + barWidth - 1, barTop, 0.0D).tex(1.0D, 0.0D).color(0xC0, 0xC0, 0xC0, 0xFF).endVertex(); + worldr.pos(barLeft, barTop, 0.0D).tex(0.0D, 0.0D).color(0xC0, 0xC0, 0xC0, 0xFF).endVertex(); + tess.draw(); + } + + GlStateManager.enableTexture(); + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.enableAlphaTest(); + GlStateManager.disableBlend(); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } + + protected void drawGradientRect(int left, int top, int right, int bottom, int color1, int color2) + { + GuiUtils.drawGradientRect(0, left, top, right, bottom, color1, color2); + } + + @Override + public List children() + { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/fml/client/gui/GuiModList.java b/src/main/java/net/minecraftforge/fml/client/gui/GuiModList.java index 722b73682a..cdfcee5d24 100644 --- a/src/main/java/net/minecraftforge/fml/client/gui/GuiModList.java +++ b/src/main/java/net/minecraftforge/fml/client/gui/GuiModList.java @@ -32,8 +32,6 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nullable; - import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -47,9 +45,8 @@ import net.minecraft.client.gui.widget.button.Button; import net.minecraft.client.gui.widget.list.ExtendedList; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.gui.widget.Widget; import net.minecraft.client.gui.RenderComponentsUtil; -import net.minecraft.client.renderer.Rectangle2d; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.texture.DynamicTexture; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.texture.TextureManager; @@ -58,6 +55,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; +import net.minecraftforge.client.gui.ScrollPanel; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.util.Size2i; import net.minecraftforge.fml.ForgeI18n; @@ -126,139 +124,142 @@ public class GuiModList extends Screen this.unsortedMods = Collections.unmodifiableList(this.mods); } - class InfoPanel extends ExtendedList { - InfoPanel(Minecraft mcIn, int widthIn, int heightIn, int topIn, int bottomIn, int slotHeightIn) + class InfoPanel extends ScrollPanel { + private ResourceLocation logoPath; + private Size2i logoDims = new Size2i(0, 0); + private List lines = Collections.emptyList(); + + InfoPanel(Minecraft mcIn, int widthIn, int heightIn, int topIn) { - super(mcIn, widthIn, heightIn, topIn, bottomIn, slotHeightIn); + super(mcIn, widthIn, heightIn, topIn, modList.getLeft() + 10); + } + + void setInfo(List lines, ResourceLocation logoPath, Size2i logoDims) + { + this.logoPath = logoPath; + this.logoDims = logoDims; + this.lines = resizeContent(lines); + } + + void clearInfo() + { + this.logoPath = null; + this.logoDims = new Size2i(0, 0); + this.lines = Collections.emptyList(); + } + + private List resizeContent(List lines) + { + List ret = new ArrayList(); + for (String line : lines) + { + if (line == null) + { + ret.add(null); + continue; + } + + ITextComponent chat = ForgeHooks.newChatWithLinks(line, false); + int maxTextLength = this.width - 12; + if (maxTextLength >= 0) + { + ret.addAll(RenderComponentsUtil.splitText(chat, maxTextLength, GuiModList.this.font, false, true)); + } + } + return ret; } @Override - protected int getScrollbarPosition() + public int getContentHeight() { - return this.getRight() - 6; + int height = 50; + height += (lines.size() * font.FONT_HEIGHT); + if (height < this.bottom - this.top - 8) + height = this.bottom - this.top - 8; + return height; } @Override - public int getRowWidth() { - return this.width; + protected int getScrollAmount() + { + return font.FONT_HEIGHT * 3; } - void setInfo(Info info) + @Override + protected void drawPanel(int entryRight, int relativeY, Tessellator tess, int mouseX, int mouseY) { - this.clearEntries(); - this.addEntry(info); - } - - public void clear() - { - this.clearEntries(); - } - class Info extends ExtendedList.AbstractListEntry - { - private ResourceLocation logoPath; - private Size2i logoDims; - private List lines; - - public Info(ExtendedList parent, List lines, @Nullable ResourceLocation logoPath, Size2i logoDims) - { - this.list = parent; - this.lines = resizeContent(lines); - this.logoPath = logoPath; - this.logoDims = logoDims; + if (logoPath != null) { + Minecraft.getInstance().getTextureManager().bindTexture(logoPath); + GlStateManager.enableBlend(); + GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); + // Draw the logo image inscribed in a rectangle with width entryWidth (minus some padding) and height 50 + int headerHeight = 50; + GuiUtils.drawInscribedRect(left, relativeY, width - 5, headerHeight, logoDims.width, logoDims.height, false, true); + relativeY += headerHeight; } - private List resizeContent(List lines) + for (ITextComponent line : lines) { - List ret = new ArrayList(); - for (String line : lines) - { - if (line == null) - { - ret.add(null); - continue; - } - - ITextComponent chat = ForgeHooks.newChatWithLinks(line, false); - int maxTextLength = InfoPanel.this.width - 8; - if (maxTextLength >= 0) - { - ret.addAll(RenderComponentsUtil.splitText(chat, maxTextLength, GuiModList.this.font, false, true)); - } - } - return ret; - } - - @Override - public void render(int entryIdx, int top, int left, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean p_194999_5_, float partialTicks) - { - if (logoPath != null) { - Minecraft.getInstance().getTextureManager().bindTexture(logoPath); - GlStateManager.enableBlend(); - GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); - // Draw the logo image inscribed in a rectangle with width entryWidth (minus some padding) and height 50 - int headerHeight = 50; - GuiUtils.drawInscribedRect(left, top, entryWidth - 5, headerHeight, logoDims.width, logoDims.height, false, true); - top += headerHeight; - } - - for (ITextComponent line : lines) - { - if (line != null) - { - GlStateManager.enableBlend(); - GuiModList.this.font.drawStringWithShadow(line.getFormattedText(), left + 4, top, 0xFFFFFF); - GlStateManager.disableAlphaTest(); - GlStateManager.disableBlend(); - } - top += font.FONT_HEIGHT; - } - - final ITextComponent component = findTextLine(mouseX, mouseY, 0, 0); - if (component!=null) { - GuiModList.this.renderComponentHoverEffect(component, mouseX, mouseY); - } - } - - private ITextComponent findTextLine(final int mouseX, final int mouseY, final int offX, final int offY) { - int offset = mouseY - offY; - if (logoPath != null) { - offset -= logoDims.height + 10; - } - if (offset <= 0) - return null; - - int lineIdx = offset / font.FONT_HEIGHT; - if (lineIdx >= lines.size() || lineIdx < 1) - return null; - - ITextComponent line = lines.get(lineIdx-1); if (line != null) { - int k = offX; - for (ITextComponent part : line) { - if (!(part instanceof StringTextComponent)) - continue; - k += GuiModList.this.font.getStringWidth(((StringTextComponent)part).getText()); - if (k >= mouseX) - { - return part; - } - } + GlStateManager.enableBlend(); + GuiModList.this.font.drawStringWithShadow(line.getFormattedText(), left + 4, relativeY, 0xFFFFFF); + GlStateManager.disableAlphaTest(); + GlStateManager.disableBlend(); } - return null; + relativeY += font.FONT_HEIGHT; } - @Override - public boolean mouseClicked(final double mouseX, final double mouseY, final int buttonmask) { - final ITextComponent component = findTextLine((int) mouseX, (int) mouseY, InfoPanel.this.getLeft(), InfoPanel.this.getTop()); - if (component != null) { - GuiModList.this.handleComponentClicked(component); - return true; - } - return false; + final ITextComponent component = findTextLine(mouseX, mouseY); + if (component!=null) { + GuiModList.this.renderComponentHoverEffect(component, mouseX, mouseY); } } + + private ITextComponent findTextLine(final int mouseX, final int mouseY) { + double offset = (mouseY - top) + border + scrollDistance + 1; + if (logoPath != null) { + offset -= 50; + } + if (offset <= 0) + return null; + + int lineIdx = (int) (offset / font.FONT_HEIGHT); + if (lineIdx >= lines.size() || lineIdx < 1) + return null; + + ITextComponent line = lines.get(lineIdx-1); + if (line != null) + { + int k = left + border; + for (ITextComponent part : line) { + if (!(part instanceof StringTextComponent)) + continue; + k += GuiModList.this.font.getStringWidth(((StringTextComponent)part).getText()); + if (k >= mouseX) + { + return part; + } + } + } + return null; + } + + @Override + public boolean mouseClicked(final double mouseX, final double mouseY, final int button) { + final ITextComponent component = findTextLine((int) mouseX, (int) mouseY); + if (component != null) { + GuiModList.this.handleComponentClicked(component); + return true; + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + protected void drawBackground() { + } } + @Override public void init() { @@ -273,8 +274,7 @@ public class GuiModList extends Screen this.modList.setLeftPos(6); int modInfoWidth = this.width - this.listWidth - 20; - this.modInfo = new InfoPanel(this.minecraft, modInfoWidth, this.height, 10, this.height - 30, this.height - 46); - this.modInfo.setLeftPos(this.listWidth + 14); + this.modInfo = new InfoPanel(this.minecraft, modInfoWidth, this.height - 30, 10); int doneButtonWidth = Math.min(modInfoWidth, 200); this.addButton(new Button(((modList.getWidth() + 8 + this.width - doneButtonWidth) / 2), this.height - 24, doneButtonWidth, 20, @@ -334,8 +334,7 @@ public class GuiModList extends Screen if (selected != null) { selected = modList.children().stream().filter(e -> e.getInfo() == selected.getInfo()).findFirst().orElse(null); - if (selected == null) - modInfo.clear(); + updateCache(); } sorted = true; } @@ -369,9 +368,8 @@ public class GuiModList extends Screen public void render(int mouseX, int mouseY, float partialTicks) { this.modList.render(mouseX, mouseY, partialTicks); - this.modInfo.render(mouseX, mouseY, partialTicks); - - int left = ((this.width - this.listWidth - 38) / 2) + this.listWidth + 30; + if (this.modInfo != null) + this.modInfo.render(mouseX, mouseY, partialTicks); String text = I18n.format("fml.menu.mods.search"); int x = ((modList.getLeft()) / 2) - (getFontRenderer().getStringWidth(text) / 2); @@ -399,8 +397,8 @@ public class GuiModList extends Screen private void updateCache() { if (selected == null) { - modInfo.clear(); this.configButton.active = false; + this.modInfo.clearInfo(); return; } ModInfo selectedMod = selected.getInfo(); @@ -465,7 +463,7 @@ public class GuiModList extends Screen } } - modInfo.setInfo(modInfo.new Info(modInfo, lines, logoData.getLeft(), logoData.getRight())); + modInfo.setInfo(lines, logoData.getLeft(), logoData.getRight()); } @Override