From eb9c9660959210114cdad62fbc5f00fa6152ddb8 Mon Sep 17 00:00:00 2001 From: tterrag Date: Sat, 23 May 2020 16:00:22 -0400 Subject: [PATCH] Improve look of notification GUI, improve StartupQuery API --- .../net/minecraftforge/fml/StartupQuery.java | 92 +++++++++++++++--- .../client/gui/screen/ConfirmationScreen.java | 6 +- .../client/gui/screen/NotificationScreen.java | 95 +++++++++++++++---- .../minecraftforge/registries/GameData.java | 36 ++++--- 4 files changed, 181 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/minecraftforge/fml/StartupQuery.java b/src/main/java/net/minecraftforge/fml/StartupQuery.java index de36cc8156..a4d8d30642 100644 --- a/src/main/java/net/minecraftforge/fml/StartupQuery.java +++ b/src/main/java/net/minecraftforge/fml/StartupQuery.java @@ -25,6 +25,15 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Supplier; +import javax.annotation.Nullable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +import com.google.common.base.Strings; + import net.minecraft.client.Minecraft; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; @@ -34,31 +43,74 @@ import net.minecraftforge.fml.client.gui.screen.NotificationScreen; import net.minecraftforge.fml.common.thread.EffectiveSide; import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.fml.server.ServerLifecycleHooks; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.MarkerManager; - -import javax.annotation.Nullable; public class StartupQuery { + + public static class QueryBuilder + { + private String header = ""; + private String text = ""; + private String action = ""; + + QueryBuilder() {} + + public QueryBuilder header(String header) + { + this.header = Strings.nullToEmpty(header); + return this; + } + + public QueryBuilder text(String text) + { + this.text = Strings.nullToEmpty(text); + return this; + } + + public QueryBuilder action(String action) + { + this.action = Strings.nullToEmpty(action); + return this; + } + + public boolean confirm() + { + return build(new AtomicBoolean()).getResult(); + } + + public void notification() + { + build(null); + } + + private StartupQuery build(AtomicBoolean result) + { + StartupQuery query = new StartupQuery(header, text, action, new AtomicBoolean()); + query.execute(); + return query; + } + } + // internal class/functionality, do not use private static final Logger LOGGER = LogManager.getLogger(); private static final Marker SQ = MarkerManager.getMarker("STARTUPQUERY"); + public static QueryBuilder builder() + { + return new QueryBuilder(); + } + + @Deprecated // TODO 1.16 remove public static boolean confirm(String text) { - StartupQuery query = new StartupQuery(text, new AtomicBoolean()); - query.execute(); - return query.getResult(); + return builder().text(text).confirm(); } private InterruptedException exception; + @Deprecated // TODO 1.16 remove public static void notify(String text) { - StartupQuery query = new StartupQuery(text, null); - query.execute(); + builder().text(text).notification(); } public static void abort() @@ -116,9 +168,11 @@ public class StartupQuery { private static volatile boolean aborted = false; - private StartupQuery(String text, @Nullable AtomicBoolean result) + private StartupQuery(String header, String text, String action, @Nullable AtomicBoolean result) { + this.header = header; this.text = text; + this.action = action; this.result = result; } @@ -133,11 +187,21 @@ public class StartupQuery { this.result.set(result); } + public String getHeader() + { + return header; + } + public String getText() { return text; } + public String getAction() + { + return action; + } + public boolean isSynchronous() { return synchronous; @@ -194,7 +258,9 @@ public class StartupQuery { } } - private String text; + private final String header; + private final String text; + private final String action; @Nullable private AtomicBoolean result; private CountDownLatch signal = new CountDownLatch(1); diff --git a/src/main/java/net/minecraftforge/fml/client/gui/screen/ConfirmationScreen.java b/src/main/java/net/minecraftforge/fml/client/gui/screen/ConfirmationScreen.java index 37f817fb9a..140b7cb5a9 100644 --- a/src/main/java/net/minecraftforge/fml/client/gui/screen/ConfirmationScreen.java +++ b/src/main/java/net/minecraftforge/fml/client/gui/screen/ConfirmationScreen.java @@ -30,16 +30,16 @@ public class ConfirmationScreen extends NotificationScreen } @Override - public void init() + protected void addConfirmationButtons() { - this.addButton(new Button(this.width / 2 - 104, this.height - 38, 100, 20, ForgeI18n.parseMessage("gui.yes"), b -> + this.addButton(new Button(this.width / 2 - 104, this.height - PADDING - 20, 100, 20, ForgeI18n.parseMessage("gui.yes"), b -> { ConfirmationScreen.this.minecraft.currentScreen = null; query.setResult(true); query.finish(); } )); - this.addButton(new Button(this.width / 2 + 4, this.height - 38, 100, 20, ForgeI18n.parseMessage("gui.no"), b -> + this.addButton(new Button(this.width / 2 + 4, this.height - PADDING - 20, 100, 20, ForgeI18n.parseMessage("gui.no"), b -> { ConfirmationScreen.this.minecraft.currentScreen = null; query.setResult(false); diff --git a/src/main/java/net/minecraftforge/fml/client/gui/screen/NotificationScreen.java b/src/main/java/net/minecraftforge/fml/client/gui/screen/NotificationScreen.java index 5b96f14fc5..94a0c91a83 100644 --- a/src/main/java/net/minecraftforge/fml/client/gui/screen/NotificationScreen.java +++ b/src/main/java/net/minecraftforge/fml/client/gui/screen/NotificationScreen.java @@ -20,23 +20,81 @@ package net.minecraftforge.fml.client.gui.screen; import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.resources.I18n; import net.minecraft.util.text.TranslationTextComponent; +import net.minecraftforge.client.gui.ScrollPanel; import net.minecraftforge.fml.StartupQuery; public class NotificationScreen extends Screen { + private class TextPanel extends ScrollPanel + { + TextPanel(Minecraft client, int width, int height, int top, int left) + { + super(client, width, height, top, left); + } + + @Override + protected int getContentHeight() + { + int height = 0; + height += (textLines.length * font.FONT_HEIGHT) + 4; + if (height < this.height - 50) + height = this.height - 50; + return height; + } + + @Override + protected void drawPanel(int entryRight, int relativeY, Tessellator tess, int mouseX, int mouseY) + { + drawCenteredLines(relativeY, textLines); + } + + @Override + protected int getScrollAmount() + { + return font.FONT_HEIGHT * 3; + } + } + + protected static final int PADDING = 6; + + protected final StartupQuery query; + + private final String[] headerLines; + private final String[] textLines; + private final String action; + + private ScrollPanel textPanel; + public NotificationScreen(StartupQuery query) { super(new TranslationTextComponent("fml.menu.notification.title")); this.query = query; + this.headerLines = query.getHeader().isEmpty() ? new String[0] : query.getHeader().split("\n"); + this.textLines = query.getText().split("\n"); + this.action = query.getAction(); } @Override public void init() { - this.buttons.add(new Button(this.width / 2 - 100, this.height - 38, 200, 20, I18n.format("gui.done"), b -> { + super.init(); + int panelY = PADDING + headerLines.length * font.FONT_HEIGHT + PADDING; + int panelHeight = this.height - PADDING - 20 - panelY; + if (!action.isEmpty()) { + panelHeight = panelHeight - font.FONT_HEIGHT - PADDING; + } + textPanel = new TextPanel(this.minecraft, this.width - (PADDING * 2), panelHeight, panelY, PADDING); + this.children.add(textPanel); + addConfirmationButtons(); + } + + protected void addConfirmationButtons() { + this.buttons.add(new Button(this.width / 2 - 100, this.height - PADDING - 20, 200, 20, I18n.format("gui.done"), b -> { NotificationScreen.this.minecraft.displayGuiScreen(null); query.finish(); })); @@ -47,29 +105,28 @@ public class NotificationScreen extends Screen { this.renderBackground(); - String[] lines = query.getText().split("\n"); + drawCenteredLines(PADDING, headerLines); - int spaceAvailable = this.height - 38 - 20; - int spaceRequired = Math.min(spaceAvailable, 10 + 10 * lines.length); - - int offset = 10 + (spaceAvailable - spaceRequired) / 2; // vertically centered - - for (String line : lines) + if (textPanel != null) { - if (offset >= spaceAvailable) - { - this.drawCenteredString(this.font, "...", this.width / 2, offset, 0xFFFFFF); - break; - } - else - { - if (!line.isEmpty()) this.drawCenteredString(this.font, line, this.width / 2, offset, 0xFFFFFF); - offset += 10; - } + textPanel.render(mouseX, mouseY, partialTicks); + } + + if (!action.isEmpty()) + { + drawCenteredString(font, action, this.width / 2, this.height - PADDING - 20 - font.FONT_HEIGHT, -1); } super.render(mouseX, mouseY, partialTicks); } - protected final StartupQuery query; + protected void drawCenteredLines(int yStart, String... lines) + { + for (String line : lines) + { + if (!line.isEmpty()) + this.drawCenteredString(font, line, this.width / 2, yStart, 0xFFFFFF); + yStart += font.FONT_HEIGHT; + } + } } diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 17b58fb7d4..9e14ed1b0b 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -99,8 +99,6 @@ import java.util.stream.Collectors; import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES; -import net.minecraftforge.fml.common.EnhancedRuntimeException.WrappedPrintStream; - /** * INTERNAL ONLY * MODDERS SHOULD HAVE NO REASON TO USE THIS CLASS @@ -728,16 +726,23 @@ public class GameData List missingRegs = snapshot.keySet().stream().filter(name -> !RegistryManager.ACTIVE.registries.containsKey(name)).collect(Collectors.toList()); if (missingRegs.size() > 0) { - String text = "Forge Mod Loader detected missing/unknown registrie(s).\n\n" + + String header = "Forge Mod Loader detected missing/unknown registrie(s).\n\n" + "There are " + missingRegs.size() + " missing registries in this save.\n" + "If you continue the missing registries will get removed.\n" + - "This may cause issues, it is advised that you create a world backup before continuing.\n\n" + - "Missing Registries:\n"; + "This may cause issues, it is advised that you create a world backup before continuing.\n\n"; + + StringBuilder text = new StringBuilder("Missing Registries:\n"); for (ResourceLocation s : missingRegs) - text += s.toString() + "\n"; + text.append(s).append("\n"); - if (!StartupQuery.confirm(text)) + boolean confirmed = StartupQuery.builder() + .header(header) + .text(text.toString()) + .action("Continue anyway?") + .confirm(); + + if (!confirmed) StartupQuery.abort(); } } @@ -816,19 +821,24 @@ public class GameData if (!defaulted.isEmpty()) { - StringBuilder buf = new StringBuilder(); - buf.append("Forge Mod Loader detected missing registry entries.\n\n") - .append("There are ").append(defaulted.size()).append(" missing entries in this save.\n") - .append("If you continue the missing entries will get removed.\n") - .append("A world backup will be automatically created in your saves directory.\n\n"); + String header = "Forge Mod Loader detected missing registry entries.\n\n" + + "There are " + defaulted.size() + " missing entries in this save.\n" + + "If you continue the missing entries will get removed.\n" + + "A world backup will be automatically created in your saves directory.\n\n"; + StringBuilder buf = new StringBuilder(); defaulted.asMap().forEach((name, entries) -> { buf.append("Missing ").append(name).append(":\n"); entries.forEach(rl -> buf.append(" ").append(rl).append("\n")); + buf.append("\n"); }); - boolean confirmed = StartupQuery.confirm(buf.toString()); + boolean confirmed = StartupQuery.builder() + .header(header) + .text(buf.toString()) + .action("Remove entries and continue?") + .confirm(); if (!confirmed) StartupQuery.abort();