Rework GuiModList mod info to use custom scrolling panel impl

This commit is contained in:
tterrag1098 2019-07-10 20:09:56 -07:00
parent ce28c5794d
commit 0a7f5ff9fa
2 changed files with 392 additions and 124 deletions

View File

@ -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<? extends IGuiEventListener> children()
{
return Collections.emptyList();
}
}

View File

@ -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.Info> {
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<ITextComponent> 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<String> 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<ITextComponent> resizeContent(List<String> lines)
{
List<ITextComponent> ret = new ArrayList<ITextComponent>();
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<Info>
{
private ResourceLocation logoPath;
private Size2i logoDims;
private List<ITextComponent> lines;
public Info(ExtendedList<Info> parent, List<String> 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<ITextComponent> resizeContent(List<String> lines)
for (ITextComponent line : lines)
{
List<ITextComponent> ret = new ArrayList<ITextComponent>();
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