Improve look of notification GUI, improve StartupQuery API

This commit is contained in:
tterrag 2020-05-23 16:00:22 -04:00
parent f0bab0fb38
commit eb9c966095
4 changed files with 181 additions and 48 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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<ResourceLocation> 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();