From 1983105bb2c75d5742a5743714460d68691f655b Mon Sep 17 00:00:00 2001 From: syeyoung Date: Thu, 17 Nov 2022 01:23:07 +0900 Subject: - Tooltips or notifications. Signed-off-by: syeyoung --- .../kr/syeyoung/dungeonsguide/launcher/Main.java | 15 ++- .../dungeonsguide/launcher/auth/AuthManager.java | 54 ++++++++- .../launcher/gui/tooltip/Notification.java | 19 +++ .../launcher/gui/tooltip/NotificationManager.java | 129 +++++++++++++++++++++ 4 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/Notification.java create mode 100644 loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/NotificationManager.java (limited to 'loader/src/main/java/kr/syeyoung/dungeonsguide') diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java index e4696dd1..6401f0fd 100755 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java @@ -24,11 +24,13 @@ import kr.syeyoung.dungeonsguide.launcher.exceptions.DungeonsGuideLoadingExcepti import kr.syeyoung.dungeonsguide.launcher.exceptions.NoSuitableLoaderFoundException; import kr.syeyoung.dungeonsguide.launcher.exceptions.NoVersionFoundException; import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; -import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.AuthenticationUnavailableException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.PrivacyPolicyRequiredException; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiDisplayer; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiLoadingError; +import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiPrivacyPolicy; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiReferenceLeak; -import kr.syeyoung.dungeonsguide.launcher.gui.screen.SpecialGuiScreen; +import kr.syeyoung.dungeonsguide.launcher.gui.tooltip.Notification; +import kr.syeyoung.dungeonsguide.launcher.gui.tooltip.NotificationManager; import kr.syeyoung.dungeonsguide.launcher.loader.IDGLoader; import kr.syeyoung.dungeonsguide.launcher.loader.JarLoader; import kr.syeyoung.dungeonsguide.launcher.loader.LocalLoader; @@ -77,6 +79,8 @@ public class Main public void initEvent(FMLInitializationEvent initializationEvent) throws ClassNotFoundException, InstantiationException, IllegalAccessException { MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(GuiDisplayer.INSTANCE); + MinecraftForge.EVENT_BUS.register(NotificationManager.INSTANCE); + try { File f = new File(configDir, "loader.cfg"); @@ -118,6 +122,13 @@ public class Main for (DungeonsGuideReloadListener listener : listeners) { listener.onLoad(dgInterface); } + + + NotificationManager.INSTANCE.updateNotification(UUID.randomUUID(), Notification.builder() + .title("Dungeons Guide Loaded!") + .description("Successfully Loaded DugneonsGuide!\nLoader: "+currentLoader.loaderName()+"\nVersion: "+currentLoader.version()) + .titleColor(0xFF00FF00) + .build()); } public void reload(IDGLoader newLoader) { try { diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java index 27a0b6dc..2dea4bdf 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java @@ -8,12 +8,16 @@ import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.AuthFailedExeption; import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.AuthenticationUnavailableException; import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.PrivacyPolicyRequiredException; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiDisplayer; +import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiLoadingError; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiPrivacyPolicy; +import kr.syeyoung.dungeonsguide.launcher.gui.tooltip.Notification; +import kr.syeyoung.dungeonsguide.launcher.gui.tooltip.NotificationManager; import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.UUID; import java.util.concurrent.*; @@ -80,6 +84,8 @@ public class AuthManager { private volatile boolean reauthLock = false; + private static final UUID authenticationFailure = UUID.randomUUID(); + private static final UUID privacyPolicyRequired = UUID.randomUUID(); AuthToken reAuth() { if (reauthLock) { @@ -99,10 +105,32 @@ public class AuthManager { GuiDisplayer.INSTANCE.displayGui(new GuiPrivacyPolicy()); throw new PrivacyPolicyRequiredException(); } + + + NotificationManager.INSTANCE.removeNotification(authenticationFailure); + NotificationManager.INSTANCE.removeNotification(privacyPolicyRequired); } catch (Exception e) { currentToken = new FailedAuthToken(e); - // TODO: loader notifications on bottom right? -// ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true); + if (e instanceof PrivacyPolicyRequiredException) { + NotificationManager.INSTANCE.updateNotification(authenticationFailure, Notification.builder() + .title("Privacy Policy") + .description("Please accept Dungeons Guide\nPrivacy Policy to enjoy server based\nfeatures of Dungeons Guide\n\n(Including Auto-Update/Remote-Jar)") + .titleColor(0xFFFF0000) + .unremovable(true) + .onClick(() -> { + GuiDisplayer.INSTANCE.displayGui(new GuiPrivacyPolicy()); + }) + .build()); + } else { + NotificationManager.INSTANCE.updateNotification(authenticationFailure, Notification.builder() + .title("Auth Error") + .description("Authentication Error Occured\n"+e.getMessage()) + .titleColor(0xFFFF0000) + .onClick(() -> { + GuiDisplayer.INSTANCE.displayGui(new GuiLoadingError(e)); + }) + .build()); + } logger.error("Re-auth failed with message {}, trying again in a 2 seconds", String.valueOf(Throwables.getRootCause(e))); throw new AuthFailedExeption(e); } finally { @@ -120,12 +148,32 @@ public class AuthManager { if (currentToken instanceof PrivacyPolicyRequiredToken) { reauthLock = true; + NotificationManager.INSTANCE.removeNotification(privacyPolicyRequired); try { currentToken = DgAuthUtil.acceptNewPrivacyPolicy(currentToken.getToken()); if (currentToken instanceof PrivacyPolicyRequiredToken) throw new PrivacyPolicyRequiredException(); } catch (Exception e) { currentToken = new FailedAuthToken(e); - // TODO: loader notifications on bottom right? + if (e instanceof PrivacyPolicyRequiredException) { + NotificationManager.INSTANCE.updateNotification(authenticationFailure, Notification.builder() + .title("Privacy Policy") + .description("Please accept Dungeons Guide\nPrivacy Policy to enjoy server based\nfeatures of Dungeons Guide\n\n(Including Auto-Update/Remote-Jar)") + .titleColor(0xFFFF0000) + .unremovable(true) + .onClick(() -> { + GuiDisplayer.INSTANCE.displayGui(new GuiPrivacyPolicy()); + }) + .build()); + } else { + NotificationManager.INSTANCE.updateNotification(authenticationFailure, Notification.builder() + .title("Auth Error") + .description("Authentication Error Occured\n"+e.getMessage()) + .titleColor(0xFFFF0000) + .onClick(() -> { + GuiDisplayer.INSTANCE.displayGui(new GuiLoadingError(e)); + }) + .build()); + } logger.error("Accepting Privacy Policy failed with message {}, trying again in a 2 seconds", String.valueOf(Throwables.getRootCause(e))); throw new AuthFailedExeption(e); } finally { diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/Notification.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/Notification.java new file mode 100644 index 00000000..1315cee8 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/Notification.java @@ -0,0 +1,19 @@ +package kr.syeyoung.dungeonsguide.launcher.gui.tooltip; + +import lombok.Builder; +import lombok.Data; + +import java.awt.*; + +@Data @Builder +public class Notification { + private String title; + private int titleColor = 0xFF00FF00; + private String description; + + private Runnable onClick; + private boolean unremovable; + + + private Rectangle boundRect; +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/NotificationManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/NotificationManager.java new file mode 100644 index 00000000..f3146e7f --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/tooltip/NotificationManager.java @@ -0,0 +1,129 @@ +package kr.syeyoung.dungeonsguide.launcher.gui.tooltip; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import org.lwjgl.input.Mouse; + +import java.awt.*; +import java.util.*; +import java.util.List; + +public class NotificationManager { + public static final NotificationManager INSTANCE = new NotificationManager(); + private NotificationManager() { + + } + + private final Map tooltipList = new HashMap<>(); + + public void updateNotification(UUID uid, Notification tooltip) { + tooltipList.put(uid, tooltip); + } + public void removeNotification(UUID uid) { + tooltipList.remove(uid); + } + + @SubscribeEvent + public void onGuiPostRender(GuiScreenEvent.DrawScreenEvent.Post rendered) { + ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft()); + FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; + int widthX = fr.getStringWidth("X"); + + GlStateManager.pushMatrix(); + GlStateManager.translate(sr.getScaledWidth() - 5, sr.getScaledHeight() -5, 0); + + int currY = sr.getScaledHeight() - 5; + + for (Notification tooltip : tooltipList.values()) { + int width, height; + String[] description = tooltip.getDescription().split("\n"); + width = + Math.max( + fr.getStringWidth(tooltip.getTitle()), + Arrays.stream(description).map(fr::getStringWidth).max(Integer::compareTo).orElse(300) + ) + 10; + height = description.length * fr.FONT_HEIGHT + 15 + fr.FONT_HEIGHT; + + GlStateManager.translate(0, -height, 0); + currY -= height; + + GlStateManager.pushMatrix(); + GlStateManager.translate(-width, 0, 0); + Gui.drawRect(0, 0,width,height, 0xFF23272a); + Gui.drawRect(1, 1, width-1, height-1, 0XFF2c2f33); + + if (!tooltip.isUnremovable()) { + if (rendered.mouseX >= sr.getScaledWidth() - 5 - widthX - 2 && rendered.mouseX <= sr.getScaledWidth() - 2 + && rendered.mouseY >= currY + 2 && rendered.mouseY <= currY + 2 + fr.FONT_HEIGHT) { + fr.drawString("X", width - widthX - 2, 2, 0xFFFFAAAA); + } else { + fr.drawString("X", width - widthX - 2, 2, 0xFFFF0000); + } + } + + GlStateManager.translate(5,5,0); + fr.drawString(tooltip.getTitle(), 0,0, tooltip.getTitleColor()); + GlStateManager.translate(0, fr.FONT_HEIGHT + 5, 0); + int y = 0; + for (String line : description) { + fr.drawString(line, 0, y, 0xFFAAAAAA); + y += fr.FONT_HEIGHT; + } + GlStateManager.popMatrix(); + + tooltip.setBoundRect(new Rectangle( + sr.getScaledWidth() - width - 5, + currY, + width, + height + )); + + currY -= 5; + GlStateManager.translate(0, -5, 0); + } + + GlStateManager.popMatrix(); + + + } + + + @SubscribeEvent + public void onMouseInput(GuiScreenEvent.MouseInputEvent.Pre mouseInputEvent) { + ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft()); + FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; + int mouseX = Mouse.getX() / sr.getScaleFactor(); + int mouseY = (Minecraft.getMinecraft().displayHeight - Mouse.getY() +3)/ sr.getScaleFactor(); + for (Map.Entry tooltip_ : tooltipList.entrySet()) { + Notification tooltip = tooltip_.getValue(); + if (tooltip.getBoundRect() == null) continue;; + if (tooltip.getBoundRect().contains(mouseX, mouseY)) { + + mouseInputEvent.setCanceled(true); + + if (Mouse.getEventButton() == -1) return; + if (!Mouse.getEventButtonState()) return; + + int dx = mouseX - tooltip.getBoundRect().x; + int dy = mouseY - tooltip.getBoundRect().y; + + if (!tooltip.isUnremovable()) { + tooltipList.remove(tooltip_.getKey()); + } + if (dx >= tooltip.getBoundRect().width - 2 - fr.getStringWidth("X") && dx <= tooltip.getBoundRect().width - 2 + && dy >= 2 && dy <= 2 + fr.FONT_HEIGHT) { + } else { + if (tooltip.getOnClick() != null) tooltip.getOnClick().run(); + } + + return; + } + } + } + +} -- cgit