From 6c0575a03f7d3948bc0c86dccda596a7fc32b776 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 23 Jun 2025 17:03:18 -0500 Subject: [PATCH] Notifications --- .../saturnclient/saturnclient/auth/Auth.java | 8 +++ .../java/org/saturnclient/ui2/Element.java | 1 + .../org/saturnclient/ui2/SaturnScreen.java | 25 ++++++-- .../org/saturnclient/ui2/anim/Animation.java | 28 ++++++++ .../org/saturnclient/ui2/anim/FadeOut.java | 22 +++++++ .../org/saturnclient/ui2/anim/SlideX.java | 36 +++++++++++ .../saturnclient/ui2/anim/SlideXAbsolute.java | 35 ++++++++++ .../ui2/elements/Notification.java | 60 ++++++++++++++++++ .../saturnclient/ui2/resources/Textures.java | 3 + .../textures/gui/components/check.png | Bin 0 -> 1571 bytes .../textures/gui/components/circle_x.png | Bin 0 -> 1609 bytes .../textures/gui/components/info.png | Bin 0 -> 1497 bytes 12 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/saturnclient/ui2/anim/FadeOut.java create mode 100644 src/main/java/org/saturnclient/ui2/anim/SlideX.java create mode 100644 src/main/java/org/saturnclient/ui2/anim/SlideXAbsolute.java create mode 100644 src/main/java/org/saturnclient/ui2/elements/Notification.java create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/check.png create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/circle_x.png create mode 100644 src/main/resources/assets/saturnclient/textures/gui/components/info.png diff --git a/src/main/java/org/saturnclient/saturnclient/auth/Auth.java b/src/main/java/org/saturnclient/saturnclient/auth/Auth.java index e035f50..680850f 100644 --- a/src/main/java/org/saturnclient/saturnclient/auth/Auth.java +++ b/src/main/java/org/saturnclient/saturnclient/auth/Auth.java @@ -5,6 +5,9 @@ import java.util.Map; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import org.saturnclient.saturnclient.cosmetics.Hats; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; +import org.saturnclient.ui2.SaturnScreen; +import org.saturnclient.ui2.elements.Notification; +import org.saturnclient.ui2.elements.Notification.NotificationKind; import org.saturnclient.saturnclient.SaturnClient; public class Auth { @@ -109,6 +112,11 @@ public class Auth { default: if (parser.error != null) { SaturnClient.LOGGER.error("Error from the server: " + parser.error); + + if (SaturnClient.client.currentScreen instanceof SaturnScreen) { + String[] a = parser.error.replace("!", "").split(": "); + ((SaturnScreen) SaturnClient.client.currentScreen).draw(new Notification(SaturnClient.client.currentScreen.width, SaturnClient.client.currentScreen.height, NotificationKind.Error, a[0], a[1])); + } } break; } diff --git a/src/main/java/org/saturnclient/ui2/Element.java b/src/main/java/org/saturnclient/ui2/Element.java index d6a0058..4e4e00c 100644 --- a/src/main/java/org/saturnclient/ui2/Element.java +++ b/src/main/java/org/saturnclient/ui2/Element.java @@ -15,6 +15,7 @@ public class Element { public Animation animation; public float scale = 1.0f; public Function curve = Curve::easeInOutCubic; + public Integer duration = null; // Element duration, will fade out if not null public void render(RenderScope renderScope, ElementContext ctx) { } diff --git a/src/main/java/org/saturnclient/ui2/SaturnScreen.java b/src/main/java/org/saturnclient/ui2/SaturnScreen.java index f482ee6..d69b579 100644 --- a/src/main/java/org/saturnclient/ui2/SaturnScreen.java +++ b/src/main/java/org/saturnclient/ui2/SaturnScreen.java @@ -3,9 +3,9 @@ package org.saturnclient.ui2; import java.util.ArrayList; import java.util.List; -import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.mixin.DrawContextAccessor; import org.saturnclient.ui2.anim.Animation; +import org.saturnclient.ui2.anim.SlideXAbsolute; import org.saturnclient.ui2.components.ElementRenderer; import net.minecraft.client.MinecraftClient; @@ -51,6 +51,25 @@ public abstract class SaturnScreen extends Screen { element.animation.tick(progress, element); }, element.animation.duration); } + + if (element.duration != null) { + new Thread(() -> { + try { + Animation fadeOut = new SlideXAbsolute(700, -(width - element.x)); + Thread.sleep(element.duration); + fadeOut.init(element); + Animation.executeSync((Float progress) -> { + fadeOut.tick(progress, element); + }, fadeOut.duration); + synchronized (elements) { + elements.remove(element); + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + } } @Override @@ -63,8 +82,6 @@ public abstract class SaturnScreen extends Screen { Animation.execute((Float progress) -> { blurProgress = progress; }, blurDuration); - - ThemeManager.load(); } public abstract void ui(); @@ -96,7 +113,7 @@ public abstract class SaturnScreen extends Screen { renderScope.matrices.scale(0.5f, 0.5f, 0.5f); - ElementRenderer.render(elements, renderScope, mouseX, mouseY); + ElementRenderer.render(new ArrayList<>(elements), renderScope, mouseX, mouseY); renderScope.matrices.pop(); } diff --git a/src/main/java/org/saturnclient/ui2/anim/Animation.java b/src/main/java/org/saturnclient/ui2/anim/Animation.java index 6991c73..d1ed85d 100644 --- a/src/main/java/org/saturnclient/ui2/anim/Animation.java +++ b/src/main/java/org/saturnclient/ui2/anim/Animation.java @@ -44,4 +44,32 @@ public abstract class Animation { } }).start(); } + + public static void executeSync(Consumer tick, int duration) { + Function curveFunction = Curve::easeInOutCubic; + float progress = 0.0f; + double incremental = (1000 - duration) / 10000.0; + + for (int i = 0; i <= 100; i++) { + float newProgress = (float) (double) curveFunction.apply(i / 100.0); + + while (Math.abs(newProgress - progress) > 1e-6) { + if (progress < newProgress) { + progress += incremental; + if (progress > newProgress) progress = newProgress; + } else { + progress -= incremental; + if (progress < newProgress) progress = newProgress; + } + + tick.accept(newProgress); + + try { + Thread.sleep(duration / 100); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } } diff --git a/src/main/java/org/saturnclient/ui2/anim/FadeOut.java b/src/main/java/org/saturnclient/ui2/anim/FadeOut.java new file mode 100644 index 0000000..a1fb42a --- /dev/null +++ b/src/main/java/org/saturnclient/ui2/anim/FadeOut.java @@ -0,0 +1,22 @@ +package org.saturnclient.ui2.anim; + +import org.saturnclient.ui2.Element; + +public class FadeOut extends Animation { + float targetOpacity; + + public FadeOut(int duration) { + super(duration); + } + + @Override + public void tick(float progress, Element element) { + element.opacity = targetOpacity - progress; + } + + @Override + public void init(Element element) { + targetOpacity = element.opacity; + // element.opacity = 1.0f; + } +} diff --git a/src/main/java/org/saturnclient/ui2/anim/SlideX.java b/src/main/java/org/saturnclient/ui2/anim/SlideX.java new file mode 100644 index 0000000..595c412 --- /dev/null +++ b/src/main/java/org/saturnclient/ui2/anim/SlideX.java @@ -0,0 +1,36 @@ +package org.saturnclient.ui2.anim; + +import org.saturnclient.ui2.Element; + +public class SlideX extends Animation { + int offset = 20; + int maxOffset = 20; + int elementX; + + public SlideX(int duration) { + super(duration); + } + + public SlideX(int duration, int offset) { + super(duration); + this.offset = offset; + this.maxOffset = offset; + } + + public SlideX(int duration, int offset, int maxOffset) { + super(duration); + this.offset = offset; + this.maxOffset = maxOffset; // ✅ fixed + } + + @Override + public void tick(float progress, Element element) { + element.x = elementX + (int) (offset * progress); + } + + @Override + public void init(Element element) { + element.x -= offset; + elementX = element.x; + } +} diff --git a/src/main/java/org/saturnclient/ui2/anim/SlideXAbsolute.java b/src/main/java/org/saturnclient/ui2/anim/SlideXAbsolute.java new file mode 100644 index 0000000..ace746f --- /dev/null +++ b/src/main/java/org/saturnclient/ui2/anim/SlideXAbsolute.java @@ -0,0 +1,35 @@ +package org.saturnclient.ui2.anim; + +import org.saturnclient.ui2.Element; + +public class SlideXAbsolute extends Animation { + int offset = 20; + int maxOffset = 20; + int elementX; + + public SlideXAbsolute(int duration) { + super(duration); + } + + public SlideXAbsolute(int duration, int offset) { + super(duration); + this.offset = offset; + this.maxOffset = offset; + } + + public SlideXAbsolute(int duration, int offset, int maxOffset) { + super(duration); + this.offset = offset; + this.maxOffset = maxOffset; + } + + @Override + public void tick(float progress, Element element) { + element.x = elementX + (int) (offset * progress); + } + + @Override + public void init(Element element) { + elementX = element.x; + } +} diff --git a/src/main/java/org/saturnclient/ui2/elements/Notification.java b/src/main/java/org/saturnclient/ui2/elements/Notification.java new file mode 100644 index 0000000..61916d0 --- /dev/null +++ b/src/main/java/org/saturnclient/ui2/elements/Notification.java @@ -0,0 +1,60 @@ +package org.saturnclient.ui2.elements; + +import org.saturnclient.saturnclient.config.Property; +import org.saturnclient.saturnclient.config.ThemeManager; +import org.saturnclient.ui2.Element; +import org.saturnclient.ui2.ElementContext; +import org.saturnclient.ui2.RenderScope; +import org.saturnclient.ui2.anim.SlideX; +import org.saturnclient.ui2.resources.Fonts; +import org.saturnclient.ui2.resources.Textures; + +public class Notification extends Element { + private static ThemeManager theme = new ThemeManager("Notification"); + private static Property bgColor = theme.property("bg-color", Property.color(0xFF000000)); + private static Property error = theme.property("error", Property.color(0xFFbf212f)); + private static Property info = theme.property("info", Property.color(0xFF264b96)); + private static Property success = theme.property("success", Property.color(0xFF27b376)); + + public static enum NotificationKind { + Error, + Info, + Success, + } + + NotificationKind kind; + String text; + String title; + + public Notification(int width, int height, NotificationKind kind, String title, String text) { + this.kind = kind; + this.text = text; + this.title = title; + this.width = (int) Math.max(Fonts.getWidth(text, 1) * 0.6f, Fonts.getWidth(title, 1)) + 20; + this.height = 50; + this.x = width - this.width - 15; + this.y = height - this.height - 15; + this.animation = new SlideX(700, -width); + this.duration = 3000; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawRoundedRectangle(0, 0, width, height, 10, bgColor.value); + switch (kind) { + case Error: + renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value); + break; + + case Info: + renderScope.drawTexture(Textures.INFO, 9, 9, 0, 0, 15, 15, info.value); + break; + + case Success: + renderScope.drawTexture(Textures.CHECK, 9, 9, 0, 0, 15, 15, success.value); + break; + } + renderScope.drawText(0.7f, title, 32, 10, 1, -1); + renderScope.drawText(0.5f, text, 32, 28, 1, -1); + } +} diff --git a/src/main/java/org/saturnclient/ui2/resources/Textures.java b/src/main/java/org/saturnclient/ui2/resources/Textures.java index 1f62aa9..2e14228 100644 --- a/src/main/java/org/saturnclient/ui2/resources/Textures.java +++ b/src/main/java/org/saturnclient/ui2/resources/Textures.java @@ -20,6 +20,9 @@ public class Textures { public static final Identifier STORE = Identifier.of("saturnclient", "textures/gui/components/store.png"); public static final Identifier COINS = Identifier.of("saturnclient", "textures/gui/components/coins.png"); public static final Identifier SHIRT = Identifier.of("saturnclient", "textures/gui/components/shirt.png"); + public static final Identifier CIRCLE_X = Identifier.of("saturnclient", "textures/gui/components/circle_x.png"); + public static final Identifier INFO = Identifier.of("saturnclient", "textures/gui/components/info.png"); + public static final Identifier CHECK = Identifier.of("saturnclient", "textures/gui/components/check.png"); public static final Identifier LOGO = Identifier.of("saturnclient", "textures/logo/logo.png"); public static final Identifier REALISTIC_LOGO = Identifier.of("saturnclient", "textures/logo/realistic.png"); diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/check.png b/src/main/resources/assets/saturnclient/textures/gui/components/check.png new file mode 100644 index 0000000000000000000000000000000000000000..039befb30ccfc0e46a71a270be1088f5ca406c19 GIT binary patch literal 1571 zcmV+;2Hg3HP)X4m}L~DmX=A26;?_{VMv;3kr5P$El^1tnGqC}5lJu07D5do zGtBnYq-Zb7dTsPaduh@7`h1^rhx5B{?#!K;-}^n@fy2!2&bjA&zjyB3Irq%3F`%DI zqo3m+&@uvDU0q`t4CzpGPjrt&HAY{dUna*-IKJFyG+t@N;MF1k=t}@`GI|8MbxAL;&EY@2Y{lexLFCQiwcO)?XGT4+ zh3Wh==cnNCr8=V?>Pt*LACICt8k@g4A0K*eWD>y95IVi#ITHAhIQq0Vz&oGB-k0;W z=nf{17KFoy{kb1IMBqgXkMu@i91dRVP4I@qnB?x;>9mIU#}yJ_1Wv$cm1lSsogZok zcthe`&hPPJY7jNJsr0x55O{~HBaF=xbW*y|b^(h9QUmH_Em%ee3DDsTE4uC3_~&_QdvRsts-7@yO2{^Wc+aQ&sW^bSBkte!N6-(s{|tHBdTqp1|43%!gOD~n># zN4eE5#_o9fX|^;GP~M^Z-pL}2<`g+Ea~&WTV6SY}iPrU@#L=@B6#CuG#o5L{xz{|O zz-k)oIy)1id&>YU0O5m|c_%s~f`Gt_gp*bDImsvR77Yg*qX`&^RBd4dbtjdz`ZARm z4;KZXzrfn5wvK)yeFECI?rxk7$LPZd=M%A6fND-Jpw9!gPY!r(Tox7i*L8g8y`=Y( zlTP|}?*{EdtfyNmk&}TK{gD>{$D4CqxzS$6E(Mk`=bc$J_=JFxJLN-8Hh^tgpTGth zwlPM7FxnVFKrgw$o}GL6?E_m0d_rIl$KOnhjbKw-(;ZmP?|qDsRtL4^N?^|r>Qidu zofBfHRAaM&^GL@vZ%)*UGtBRvC0VWho@6V;Wvvt|1X zsH8E;7)`-wd7<~jAZNbIqSDBlJK*PN%fP16P7{Io{I>hgn=sO5K3W1KYJpd9{4V4$ z*8sT+Y}fk)w1rfq%t^XbMKjnGY!(MMRRrG8>C3q5w#BC^9x7Kl(avyP6M@7~+s5sU z;Rp;>@98rEyk4$)$vXK3OzC#hPp9=(JkuC2##kfTX99r^f=@R9mVzlwuYCfl&8#uT zeZ8UCA1v>q7H8A8GHXluEFH!h5-VY>bfN9S=s!o$qfOw|V3}ZnWD(Gw)2}|vqGMiN zwB_Ui@0L;EP`M|SP+3KpUw68i$iEzQ>!Mz4b=d%Z96p{wZCOR%5mXdzk`MuXUC~z3 z?t+v|jmAlD7q<3GW`M2EeNWpV#Qr*pfQ%RgeJ;}TU`vZX@p~eU?E9kbfT#VFkyl3G zmlnskbw8RwLgH+W_j#RE+?|cXj+P+%Mw@cTe=Cp0>t{qbE~Q{GhN1BY_0^8x^8;M2eRJxrqrBqWq-Yt;3* zC`;a^QL9(_i3tGTa0ds{raA3CF{yB%(YEDF>rW+=NwLpaG=YSKRwZSDN-zAmns=vT zu4$=O3~(4xfmVR2;-lXa_#eo< V1y7nv`se@v002ovPDHLkV1kDv`5*uQ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/saturnclient/textures/gui/components/circle_x.png b/src/main/resources/assets/saturnclient/textures/gui/components/circle_x.png new file mode 100644 index 0000000000000000000000000000000000000000..11401deddf697b462a1e396a4fdc30d91fe3a3c2 GIT binary patch literal 1609 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 literal 0 HcmV?d00001