From 6c9b08c366c54aa269b643eab218765064052297 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 18:46:12 +0200 Subject: [PATCH 001/661] add StyleValueType --- .../snackbag/vera/style/StyleValueType.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/StyleValueType.java diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java new file mode 100644 index 00000000..fd06bd43 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -0,0 +1,20 @@ +package net.snackbag.vera.style; + +import net.snackbag.vera.core.VColor; + +public enum StyleValueType { + COLOR, + STRING, + INT, + FLOAT; + + StyleValueType() {} + + public static StyleValueType get(Object val) { + if (val instanceof VColor) return COLOR; + else if (val instanceof String) return STRING; + else if (val instanceof Integer) return INT; + else if (val instanceof Float) return FLOAT; + else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); + } +} From 17a7f86dc09245eb16b86be25cb561484c5beb7c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 18:46:21 +0200 Subject: [PATCH 002/661] add basic VStyleSheet --- .../java/net/snackbag/vera/style/VStyleSheet.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/VStyleSheet.java diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java new file mode 100644 index 00000000..6ad59492 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -0,0 +1,11 @@ +package net.snackbag.vera.style; + +import net.snackbag.vera.widget.VWidget; + +import java.util.HashMap; + +public class VStyleSheet { + private final HashMap, HashMap> widgetSpecificStyles = new HashMap<>(); + private final HashMap styleClasses = new HashMap<>(); + private final HashMap typeRegistry = new HashMap<>(); +} From a9baa3f73a6d46f6017850db93468a6210807252 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:18:58 +0200 Subject: [PATCH 003/661] add classes --- src/main/java/net/snackbag/vera/widget/VWidget.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index d31bb957..c45ce775 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -6,9 +6,7 @@ import org.jetbrains.annotations.Nullable; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.function.Supplier; public abstract class VWidget> { @@ -39,6 +37,7 @@ public abstract class VWidget> { private final HashMap> eventExecutors; private final List> visibilityConditions; + public final SortedSet classes = new TreeSet<>(); public VWidget(int x, int y, int width, int height, VeraApp app) { this.x = x; From bf1a4e9774338aa8206e4692ae56becea62eef87 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:19:09 +0200 Subject: [PATCH 004/661] basic stylesheet --- .../net/snackbag/vera/style/VStyleSheet.java | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 6ad59492..9949bdb9 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -1,11 +1,73 @@ package net.snackbag.vera.style; import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.SortedSet; public class VStyleSheet { private final HashMap, HashMap> widgetSpecificStyles = new HashMap<>(); - private final HashMap styleClasses = new HashMap<>(); + private final HashMap> styleClasses = new HashMap<>(); private final HashMap typeRegistry = new HashMap<>(); + + public VStyleSheet() { + // Generic + reserveType("color", StyleValueType.COLOR); + reserveType("background-color", StyleValueType.COLOR); + + // Font + reserveType("font-size", StyleValueType.INT); + reserveType("font-name", StyleValueType.STRING); + } + + public T getKey(VWidget widget, String key) { + if (widgetSpecificStyles.containsKey(widget) && widgetSpecificStyles.get(widget).containsKey(key)) { + return (T) widgetSpecificStyles.get(widget).get(key); + } + + HashMap mixed = mixClasses(widget.classes); + + if (mixed.containsKey(key)) { + return (T) mixed.get(key); + } + + return null; + } + + public void setKey(VWidget widget, String key, Object value) { + StyleValueType res = getReservation(key); + StyleValueType valRes = StyleValueType.get(value); + + if (res != null) { + if (valRes != res) + throw new RuntimeException("Cannot set key %s, because it is reserved for type %s. Received: %s".formatted(key, res, valRes)); + } else reserveType(key, valRes); + + if (!widgetSpecificStyles.containsKey(widget)) widgetSpecificStyles.put(widget, new HashMap<>()); + widgetSpecificStyles.get(widget).put(key, value); + } + + public void reserveType(String key, StyleValueType type) { + typeRegistry.put(key, type); + } + + public @Nullable StyleValueType getReservation(String key) { + return typeRegistry.getOrDefault(key, null); + } + + public HashMap getClassStyles(String clazz) { + return styleClasses.getOrDefault(clazz, new HashMap<>()); + } + + public HashMap mixClasses(SortedSet classes) { + final HashMap values = new HashMap<>(); + + for (String clazz : classes) { + HashMap styles = getClassStyles(clazz); + for (String key : styles.keySet()) values.put(key, styles.get(key)); + } + + return values; + } } From dce6703aeface89b32869c02832f8a2195648086 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:19:14 +0200 Subject: [PATCH 005/661] add standard value --- .../net/snackbag/vera/style/StyleValueType.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index fd06bd43..dfb3e332 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -3,12 +3,16 @@ import net.snackbag.vera.core.VColor; public enum StyleValueType { - COLOR, - STRING, - INT, - FLOAT; + COLOR(VColor.black()), + STRING(""), + INT(0), + FLOAT(0.0F); - StyleValueType() {} + public final Object standard; + + StyleValueType(Object standard) { + this.standard = standard; + } public static StyleValueType get(Object val) { if (val instanceof VColor) return COLOR; From 2660391edeb3fc6c7ca568b2667abedc4dc2b6c5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:19:51 +0200 Subject: [PATCH 006/661] remove cursorBeforeHover --- src/main/java/net/snackbag/vera/widget/VWidget.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index c45ce775..90fb8e54 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -20,7 +20,6 @@ public abstract class VWidget> { protected VeraApp app; protected VCursorShape hoverCursor = VCursorShape.DEFAULT; - protected @Nullable VCursorShape cursorBeforeHover = null; protected boolean focusOnClick = true; private boolean hovered = false; private boolean visible = true; @@ -391,7 +390,6 @@ public void handleBuiltinEvent(String event, Object... args) { } case "hover" -> { - cursorBeforeHover = app.getCursorShape(); app.setCursorShape(hoverCursor); } @@ -399,10 +397,6 @@ public void handleBuiltinEvent(String event, Object... args) { clearLeftClickDown(); clearRightClickDown(); clearMiddleClickDown(); - - if (cursorBeforeHover == null) break; - - app.setCursorShape(cursorBeforeHover); } } } From 56a315e932ecabcbda01078bda0aa997e8fc59df Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:23:05 +0200 Subject: [PATCH 007/661] mark border methods for removal --- .../net/snackbag/vera/widget/VWidget.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 90fb8e54..e5cd394f 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -3,6 +3,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.*; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -99,18 +100,26 @@ public V4Color getBorder() { return border; } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorder(V4Color border) { this.border = border; } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorder(VColor all) { setBorder(new V4Color(all)); } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorder(VColor tb, VColor lr) { setBorder(new V4Color(tb, lr)); } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorder(VColor top, VColor bottom, VColor left, VColor right) { setBorder(new V4Color(top, bottom, left, right)); } @@ -119,23 +128,33 @@ public V4Int getBorderSize() { return borderSize; } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorderSize(V4Int borderSize) { this.borderSize = borderSize; } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorderSize(int all) { setBorderSize(new V4Int(all)); } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorderSize(int tb, int lr) { setBorderSize(new V4Int(tb, lr)); } + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") public void setBorderSize(int top, int bottom, int left, int right) { setBorderSize(new V4Int(top, bottom, left, right)); } public void renderBorder() { + // TODO: [Render Rework] Better border rendering + // Top Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() - borderSize.get1(), getHitboxWidth(), borderSize.get1(), 0, border.get1()); if (borderSize.get3() > 0) { From 259c09aeaeca94bd61fd9b73c265e12b61de489f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:29:12 +0200 Subject: [PATCH 008/661] add styles --- src/main/java/net/snackbag/vera/core/VeraApp.java | 2 ++ src/main/java/net/snackbag/vera/widget/VWidget.java | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 0e218746..5b1881fe 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -3,6 +3,7 @@ import net.minecraft.client.MinecraftClient; import net.snackbag.vera.Vera; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -14,6 +15,7 @@ import java.util.stream.Collectors; public abstract class VeraApp { + public final VStyleSheet styleSheet = new VStyleSheet(); private final List> widgets; private final HashMap shortcuts; private VColor backgroundColor; diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index e5cd394f..1a48f17e 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -152,6 +152,19 @@ public void setBorderSize(int top, int bottom, int left, int right) { setBorderSize(new V4Int(top, bottom, left, right)); } + public void setStyle(String key, Object value) { + app.styleSheet.setKey(this, key, value); + } + + public V getStyle(String key) { + return app.styleSheet.getKey(this, key); + } + + public V getStyleOrDefault(String key, V dflt) { + V style = getStyle(key); + return style != null ? style : dflt; + } + public void renderBorder() { // TODO: [Render Rework] Better border rendering From db2f775dd9326d05c33d8056abbfbf8858eb22dc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:32:24 +0200 Subject: [PATCH 009/661] label is now using background color style --- .../java/net/snackbag/vera/widget/VLabel.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index e3680dea..5a2e9c9a 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -7,7 +7,6 @@ public class VLabel extends VWidget implements VPaddingWidget { private String text; private VFont font; - private VColor backgroundColor; private V4Int padding; private VAlignmentFlag alignment; @@ -16,10 +15,11 @@ public VLabel(String text, VeraApp app) { this.text = text; this.font = VFont.create(); - this.backgroundColor = VColor.transparent(); this.padding = new V4Int(4); this.focusOnClick = false; alignment = VAlignmentFlag.LEFT; + + setStyle("background-color", VColor.transparent()); } public String getText() { @@ -34,18 +34,6 @@ public void setFont(VFont font) { this.font = font; } - public VColor getBackgroundColor() { - return backgroundColor; - } - - public void setBackgroundColor(VColor backgroundColor) { - this.backgroundColor = backgroundColor; - } - - public VColor.ColorModifier modifyBackgroundColor() { - return new VColor.ColorModifier(backgroundColor, this::setBackgroundColor); - } - public void setText(String text) { this.text = text; } @@ -112,7 +100,7 @@ public void render() { getHitboxWidth(), getHitboxHeight(), rotation, - backgroundColor + getStyle("background-color") ); switch (alignment) { From 4e99ec8b7d57b869f647e04107980a64d380c374 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:32:31 +0200 Subject: [PATCH 010/661] suppress removal warnings --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 33e0458b..9b6ca866 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -25,6 +25,7 @@ public TestApplication() { } @Override + @SuppressWarnings("removal") public void init() { VShortcut exit = new VShortcut(this, "escape", () -> { if (hasFocusedWidget()) { From d633fccf71fb5a82c3a49e0eba2e24a1f634ac7c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:32:46 +0200 Subject: [PATCH 011/661] clean up imports --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 9b6ca866..407d39bd 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -1,6 +1,5 @@ package net.snackbag.mcvera.test; -import net.minecraft.client.MinecraftClient; import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VAlignmentFlag; @@ -9,13 +8,8 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.widget.*; -import org.lwjgl.PointerBuffer; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.util.tinyfd.TinyFileDialogs; -import java.awt.*; import java.nio.file.Path; -import java.util.Arrays; public class TestApplication extends VeraApp { public static final TestApplication INSTANCE = new TestApplication(); From 8328924c8dc9577f3347cc7128632c4bcaebfdce Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:32:53 +0200 Subject: [PATCH 012/661] use background color --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 407d39bd..9db46a60 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -59,7 +59,7 @@ public void init() { label.setPadding(5); label.move(10); - label.setBackgroundColor(VColor.black()); + label.setStyle("background-color", VColor.black()); label.setFont(label.getFont().withColor(VColor.white())); label.adjustSize(); label.onHover(() -> { @@ -72,7 +72,7 @@ public void init() { VLabel centerLabel = new VLabel("CENTER", this).alsoAdd(); centerLabel.setAlignment(VAlignmentFlag.CENTER); - centerLabel.setBackgroundColor(VColor.black()); + centerLabel.setStyle("background-color", VColor.black()); centerLabel.modifyFontColor().rgb(255, 255, 255); centerLabel.move(220, 10); centerLabel.setBorder(VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); @@ -81,7 +81,7 @@ public void init() { VLabel rightLabel = new VLabel("RIGHT", this).alsoAdd(); rightLabel.setAlignment(VAlignmentFlag.RIGHT); - rightLabel.setBackgroundColor(VColor.black()); + rightLabel.setStyle("background-color", VColor.black()); rightLabel.modifyFontColor().rgb(255, 255, 255); rightLabel.move(100, 10); rightLabel.setBorder(VColor.white()); From 874f409d333f9d128e5dbc4ea00c734db9b9921b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:35:34 +0200 Subject: [PATCH 013/661] add font style --- .../java/net/snackbag/vera/style/StyleValueType.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index dfb3e332..9db3e5a0 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -1,12 +1,15 @@ package net.snackbag.vera.style; import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFont; public enum StyleValueType { - COLOR(VColor.black()), STRING(""), INT(0), - FLOAT(0.0F); + FLOAT(0.0F), + + COLOR(VColor.black()), + FONT(VFont.create()); public final Object standard; @@ -15,10 +18,11 @@ public enum StyleValueType { } public static StyleValueType get(Object val) { - if (val instanceof VColor) return COLOR; - else if (val instanceof String) return STRING; + if (val instanceof String) return STRING; else if (val instanceof Integer) return INT; else if (val instanceof Float) return FLOAT; + else if (val instanceof VColor) return COLOR; + else if (val instanceof VFont) return FONT; else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); } } From a397df76b69366cab1eea510b61051a076204f45 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:36:34 +0200 Subject: [PATCH 014/661] mark font as reserved --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 9949bdb9..fae3197c 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -13,10 +13,11 @@ public class VStyleSheet { public VStyleSheet() { // Generic - reserveType("color", StyleValueType.COLOR); reserveType("background-color", StyleValueType.COLOR); // Font + reserveType("color", StyleValueType.COLOR); + reserveType("font", StyleValueType.FONT); reserveType("font-size", StyleValueType.INT); reserveType("font-name", StyleValueType.STRING); } From 8942efb7772ece6b71a620f01eacd779fb5ddfe0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:43:08 +0200 Subject: [PATCH 015/661] add modify methods --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 10 ++++++++++ src/main/java/net/snackbag/vera/widget/VWidget.java | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index fae3197c..b9c6a15b 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -1,5 +1,7 @@ package net.snackbag.vera.style; +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFont; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -49,6 +51,14 @@ public void setKey(VWidget widget, String key, Object value) { widgetSpecificStyles.get(widget).put(key, value); } + public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { + return new VColor.ColorModifier(getKey(widget, key), color -> setKey(widget, key, color)); + } + + public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key) { + return new VFont.FontModifier(getKey(widget, key), font -> setKey(widget, key, font)); + } + public void reserveType(String key, StyleValueType type) { typeRegistry.put(key, type); } diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 1a48f17e..e3a247e0 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -165,6 +165,14 @@ public V getStyleOrDefault(String key, V dflt) { return style != null ? style : dflt; } + public VColor.ColorModifier modifyKeyAsColor(String key) { + return app.styleSheet.modifyKeyAsColor(this, key); + } + + public VFont.FontModifier modifyKeyAsFont(String key) { + return app.styleSheet.modifyKeyAsFont(this, key); + } + public void renderBorder() { // TODO: [Render Rework] Better border rendering From 5ec62eed637590d13aa862ecf926fce7fe42f378 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:43:27 +0200 Subject: [PATCH 016/661] rename methods to something shorter --- src/main/java/net/snackbag/vera/widget/VWidget.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index e3a247e0..cf259704 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -165,11 +165,11 @@ public V getStyleOrDefault(String key, V dflt) { return style != null ? style : dflt; } - public VColor.ColorModifier modifyKeyAsColor(String key) { + public VColor.ColorModifier modifyColor(String key) { return app.styleSheet.modifyKeyAsColor(this, key); } - public VFont.FontModifier modifyKeyAsFont(String key) { + public VFont.FontModifier modifyFont(String key) { return app.styleSheet.modifyKeyAsFont(this, key); } From daa533a5a0a48b78e1c067f998cde855f5dd6bc4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:57:45 +0200 Subject: [PATCH 017/661] move methods --- .../java/net/snackbag/vera/widget/VLabel.java | 34 +++++++++---------- .../net/snackbag/vera/widget/VWidget.java | 8 ----- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 5a2e9c9a..7a70c72f 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -6,7 +6,6 @@ public class VLabel extends VWidget implements VPaddingWidget { private String text; - private VFont font; private V4Int padding; private VAlignmentFlag alignment; @@ -14,26 +13,18 @@ public VLabel(String text, VeraApp app) { super(0, 0, 100, 16, app); this.text = text; - this.font = VFont.create(); this.padding = new V4Int(4); this.focusOnClick = false; alignment = VAlignmentFlag.LEFT; setStyle("background-color", VColor.transparent()); + setStyle("font", VFont.create()); } public String getText() { return text; } - public VFont getFont() { - return font; - } - - public void setFont(VFont font) { - this.font = font; - } - public void setText(String text) { this.text = text; } @@ -48,14 +39,6 @@ public void setPadding(V4Int padding) { this.padding = padding; } - public VFont.FontModifier modifyFont() { - return new VFont.FontModifier(font, this::setFont); - } - - public VColor.ColorModifier modifyFontColor() { - return new VColor.ColorModifier(font.getColor(), (color) -> setFont(font.withColor(color))); - } - @Override public int getHitboxWidth() { return width + padding.get3() + padding.get4(); @@ -85,13 +68,28 @@ public void setAlignment(VAlignmentFlag alignment) { } public void adjustSize() { + VFont font = getStyle("font"); + this.width = Vera.provider.getTextWidth(text, font); this.height = Vera.provider.getTextHeight(text, font); } + public VColor.ColorModifier modifyColor(String key) { + return app.styleSheet.modifyKeyAsColor(this, key); + } + + public VFont.FontModifier modifyFont(String key) { + return app.styleSheet.modifyKeyAsFont(this, key); + } + + public VColor.ColorModifier modifyFontColor(String key) { + return app.styleSheet.modifyKeyAsColor(this, key); + } + @Override public void render() { VeraApp app = getApp(); + VFont font = getStyle("font"); Vera.renderer.drawRect( app, diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index cf259704..1a48f17e 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -165,14 +165,6 @@ public V getStyleOrDefault(String key, V dflt) { return style != null ? style : dflt; } - public VColor.ColorModifier modifyColor(String key) { - return app.styleSheet.modifyKeyAsColor(this, key); - } - - public VFont.FontModifier modifyFont(String key) { - return app.styleSheet.modifyKeyAsFont(this, key); - } - public void renderBorder() { // TODO: [Render Rework] Better border rendering From 19fffa137ba7fb5ac54f708a3d6e8e75a69ac402 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:57:49 +0200 Subject: [PATCH 018/661] states --- src/main/java/net/snackbag/vera/style/StyleState.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/StyleState.java diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/StyleState.java new file mode 100644 index 00000000..f949d35c --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/StyleState.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.style; + +public enum StyleState { + IDLE, + HOVERED, + CLICKED +} From 00ee0cb9f5dd0a184971be4b5c7657a6d7c25b4e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 20:58:57 +0200 Subject: [PATCH 019/661] move to new style methods for label --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 9db46a60..00484a58 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -60,7 +60,7 @@ public void init() { label.setPadding(5); label.move(10); label.setStyle("background-color", VColor.black()); - label.setFont(label.getFont().withColor(VColor.white())); + label.modifyFont("font").color(VColor.white()); label.adjustSize(); label.onHover(() -> { label.setText("Hovered"); @@ -73,7 +73,7 @@ public void init() { VLabel centerLabel = new VLabel("CENTER", this).alsoAdd(); centerLabel.setAlignment(VAlignmentFlag.CENTER); centerLabel.setStyle("background-color", VColor.black()); - centerLabel.modifyFontColor().rgb(255, 255, 255); + centerLabel.modifyFontColor("font").rgb(255, 255, 255); centerLabel.move(220, 10); centerLabel.setBorder(VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); centerLabel.setBorderSize(5, 10, 8, 16); @@ -82,7 +82,7 @@ public void init() { VLabel rightLabel = new VLabel("RIGHT", this).alsoAdd(); rightLabel.setAlignment(VAlignmentFlag.RIGHT); rightLabel.setStyle("background-color", VColor.black()); - rightLabel.modifyFontColor().rgb(255, 255, 255); + rightLabel.modifyFontColor("font").rgb(255, 255, 255); rightLabel.move(100, 10); rightLabel.setBorder(VColor.white()); rightLabel.setBorderSize(1); From 74bb7201eac4d8c7a2119879a8985c6dfb37e1e2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 21:29:39 +0200 Subject: [PATCH 020/661] add a few more states, not many --- .../net/snackbag/vera/style/StyleState.java | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/StyleState.java index f949d35c..069ee866 100644 --- a/src/main/java/net/snackbag/vera/style/StyleState.java +++ b/src/main/java/net/snackbag/vera/style/StyleState.java @@ -1,7 +1,43 @@ package net.snackbag.vera.style; +import org.jetbrains.annotations.Nullable; + public enum StyleState { - IDLE, - HOVERED, - CLICKED + IDLE("default"), + HOVERED("hover", IDLE), + + CLICKED("any-click", IDLE), + LEFT_CLICKED("left-click", CLICKED), + MIDDLE_CLICKED("middle-click", CLICKED), + RIGHT_CLICKED("right-click", CLICKED), + + LC_DRAGGING("lc-drag", LEFT_CLICKED), + LC_DRAG_TOP("lc-drag-top", LC_DRAGGING), + LC_DRAG_BOTTOM("lc-drag-bottom", LC_DRAGGING), + LC_DRAG_LEFT("lc-drag-left", LC_DRAGGING), + LC_DRAG_RIGHT("lc-drag-right", LC_DRAGGING), + + MC_DRAGGING("mc-drag", MIDDLE_CLICKED), + MC_DRAG_TOP("mc-drag-top", MC_DRAGGING), + MC_DRAG_BOTTOM("mc-drag-bottom", MC_DRAGGING), + MC_DRAG_LEFT("mc-drag-left", MC_DRAGGING), + MC_DRAG_RIGHT("mc-drag-right", MC_DRAGGING), + + RC_DRAGGING("rc-drag", RIGHT_CLICKED), + RC_DRAG_TOP("rc-drag-top", RC_DRAGGING), + RC_DRAG_BOTTOM("rc-drag-bottom", RC_DRAGGING), + RC_DRAG_LEFT("rc-drag-left", RC_DRAGGING), + RC_DRAG_RIGHT("rc-drag-right", RC_DRAGGING); + + public final String identifier; + public final @Nullable StyleState fallback; + + StyleState(String identifier) { + this(identifier, null); + } + + StyleState(String identifier, @Nullable StyleState fallback) { + this.identifier = identifier; + this.fallback = fallback; + } } From cb4294f9c180f6d64666a59d680a422440ff68ea Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 21:30:19 +0200 Subject: [PATCH 021/661] add test stylesheet file --- .../resources/assets/mcvera/demo/test.vss | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/resources/assets/mcvera/demo/test.vss diff --git a/src/main/resources/assets/mcvera/demo/test.vss b/src/main/resources/assets/mcvera/demo/test.vss new file mode 100644 index 00000000..1ffee70b --- /dev/null +++ b/src/main/resources/assets/mcvera/demo/test.vss @@ -0,0 +1,23 @@ +VeraApp { + background-color: (0 0 0 0.40); +} + +VLabel { + color: black; + background-color: white; + + padding: 5px 15px; + border: 1px (0 0 0); + border-opacity: 0.5; + + cursor: pointer; + transition: 0.1s; +} + +VLabel:hover { + background-filter: "sub" 20; +} + +VLabel:clicked { + background-filter: "sub" 40; +} From 2352cc9c13333f01b9a666676581fe5925a2cb07 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 21:49:46 +0200 Subject: [PATCH 022/661] prepare for drag logic rewrite --- .../net/snackbag/vera/widget/VWidget.java | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 1a48f17e..a9935b1c 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -28,12 +28,6 @@ public abstract class VWidget> { private boolean leftClickDown = false; private boolean middleClickDown = false; private boolean rightClickDown = false; - private int leftDragPreviousX = -1; - private int leftDragPreviousY = -1; - private int middleDragPreviousX = -1; - private int middleDragPreviousY = -1; - private int rightDragPreviousX = -1; - private int rightDragPreviousY = -1; private final HashMap> eventExecutors; private final List> visibilityConditions; @@ -394,37 +388,7 @@ public void handleBuiltinEvent(String event, Object... args) { case "right-click-release" -> clearRightClickDown(); case "middle-click-release" -> clearMiddleClickDown(); - case "mouse-move" -> { - if (leftClickDown) { - int newX = (int) args[0]; - int newY = (int) args[1]; - if (leftDragPreviousX != -1 || leftDragPreviousY != -1) fireEvent("mouse-drag-left", leftDragPreviousX, leftDragPreviousY, newX, newY); - - leftDragPreviousX = newX; - leftDragPreviousY = newY; - } else if (rightClickDown) { - int newX = (int) args[0]; - int newY = (int) args[1]; - - if (rightDragPreviousX != -1 || rightDragPreviousY != -1) fireEvent("mouse-drag-right", rightDragPreviousX, rightDragPreviousY, newX, newY); - - rightDragPreviousX = newX; - rightDragPreviousY = newY; - } else if (middleClickDown) { - int newX = (int) args[0]; - int newY = (int) args[1]; - - if (middleDragPreviousX != -1 || middleDragPreviousY != -1) fireEvent("mouse-drag-middle", middleDragPreviousX, middleDragPreviousY, newX, newY); - - middleDragPreviousX = newX; - middleDragPreviousY = newY; - } - } - - case "hover" -> { - app.setCursorShape(hoverCursor); - } - + case "hover" -> app.setCursorShape(hoverCursor); case "hover-leave" -> { clearLeftClickDown(); clearRightClickDown(); @@ -435,20 +399,14 @@ public void handleBuiltinEvent(String event, Object... args) { private void clearLeftClickDown() { leftClickDown = false; - leftDragPreviousX = -1; - leftDragPreviousY = -1; } private void clearRightClickDown() { rightClickDown = false; - rightDragPreviousX = -1; - rightDragPreviousY = -1; } private void clearMiddleClickDown() { middleClickDown = false; - middleDragPreviousX = -1; - middleDragPreviousY = -1; } public VCursorShape getHoverCursor() { From db738e4632a5c85f71759013a6406caa1288a55f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 21:52:46 +0200 Subject: [PATCH 023/661] add style test app --- .../mcvera/test/StyleTestApplication.java | 17 +++++++++++++++++ .../net/snackbag/mcvera/test/TestHandler.java | 6 +++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java new file mode 100644 index 00000000..b517f63f --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -0,0 +1,17 @@ +package net.snackbag.mcvera.test; + +import net.minecraft.util.Identifier; +import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.widget.VLabel; + +public class StyleTestApplication extends VeraApp { + public static final StyleTestApplication INSTANCE = new StyleTestApplication(); + + @Override + public void init() { + //loadStyleSheet(new Identifier(MinecraftVera.MOD_ID, "demo/test.vss")); + + new VLabel("helo", this).alsoAdd(); + } +} diff --git a/src/main/java/net/snackbag/mcvera/test/TestHandler.java b/src/main/java/net/snackbag/mcvera/test/TestHandler.java index 68a44829..1c4cc8dc 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestHandler.java +++ b/src/main/java/net/snackbag/mcvera/test/TestHandler.java @@ -16,7 +16,11 @@ public static void impl(boolean force) { ClientTickEvents.END_CLIENT_TICK.register((client) -> { if (InputUtil.isKeyPressed(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_APOSTROPHE)) { - TestApplication.INSTANCE.setVisibility(true); + if (InputUtil.isKeyPressed(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_SHIFT)) { + StyleTestApplication.INSTANCE.show(); + } else { + TestApplication.INSTANCE.setVisibility(true); + } } }); } From d8d74d72b1d1896e9dbf0005ac02b3f15cc696a9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 21:59:25 +0200 Subject: [PATCH 024/661] use context --- src/main/java/net/snackbag/vera/event/VMouseDragEvent.java | 5 ++++- src/main/java/net/snackbag/vera/widget/VWidget.java | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java b/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java index d22d00fd..2fa90fba 100644 --- a/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java +++ b/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java @@ -1,5 +1,8 @@ package net.snackbag.vera.event; public interface VMouseDragEvent { - void run(int startX, int startY, int currentX, int currentY); + void run(Context ctx); + + record Context(int startX, int startY, int currentX, int currentY, int moveX, int moveY) { + } } diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index a9935b1c..e2eff72e 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -292,15 +292,15 @@ public void onMouseMove(VMouseMoveEvent runnable) { } public void onMouseDragLeft(VMouseDragEvent runnable) { - registerEventExecutor("mouse-drag-left", args -> runnable.run((int) args[0], (int) args[1], (int) args[2], (int) args[3])); + registerEventExecutor("mouse-drag-left", args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragRight(VMouseDragEvent runnable) { - registerEventExecutor("mouse-drag-right", args -> runnable.run((int) args[0], (int) args[1], (int) args[2], (int) args[3])); + registerEventExecutor("mouse-drag-right", args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragMiddle(VMouseDragEvent runnable) { - registerEventExecutor("mouse-drag-middle", args -> runnable.run((int) args[0], (int) args[1], (int) args[2], (int) args[3])); + registerEventExecutor("mouse-drag-middle", args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onFocusStateChange(Runnable runnable) { From 80d621d31cde2a04f850b2521d7b63dc5bee4fad Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 22:14:59 +0200 Subject: [PATCH 025/661] fix invalid hovering --- .../java/net/snackbag/vera/core/VeraApp.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 5b1881fe..d35729db 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.util.DraggingManager; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -16,6 +17,8 @@ public abstract class VeraApp { public final VStyleSheet styleSheet = new VStyleSheet(); + + private final DraggingManager dragManager = new DraggingManager(this); private final List> widgets; private final HashMap shortcuts; private VColor backgroundColor; @@ -217,8 +220,11 @@ public List> getHoveredWidgets() { } public List> getHoveredWidgets(int mouseX, int mouseY) { + int mx = mouseX - x; + int my = mouseY - y; + return getWidgets().parallelStream() - .filter(widget -> isMouseOverWidget(widget, mouseX, mouseY)) + .filter(widget -> isMouseOverWidget(widget, mx, my)) .filter(VWidget::visibilityConditionsPassed) .collect(Collectors.toList()); } @@ -282,4 +288,16 @@ public void keyPressed(int keyCode, int scanCode, int modifiers) { public void charTyped(char chr, int modifiers) { if (hasFocusedWidget()) getFocusedWidget().charTyped(chr, modifiers); } + + public void mouseClicked(VMouseButton button, int x, int y) { + dragManager.handleClick(button, x, y); + } + + public void mouseReleased(VMouseButton button, int x, int y) { + dragManager.handleRelease(button, x, y); + } + + public void mouseMoved(int x, int y) { + dragManager.handleMove(x, y); + } } From 22a676b5958ac8e0d08935fc6240d53f02ef5820 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 22:16:15 +0200 Subject: [PATCH 026/661] no --- src/main/java/net/snackbag/vera/core/VeraApp.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index d35729db..730229f0 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -18,7 +18,6 @@ public abstract class VeraApp { public final VStyleSheet styleSheet = new VStyleSheet(); - private final DraggingManager dragManager = new DraggingManager(this); private final List> widgets; private final HashMap shortcuts; private VColor backgroundColor; @@ -288,16 +287,4 @@ public void keyPressed(int keyCode, int scanCode, int modifiers) { public void charTyped(char chr, int modifiers) { if (hasFocusedWidget()) getFocusedWidget().charTyped(chr, modifiers); } - - public void mouseClicked(VMouseButton button, int x, int y) { - dragManager.handleClick(button, x, y); - } - - public void mouseReleased(VMouseButton button, int x, int y) { - dragManager.handleRelease(button, x, y); - } - - public void mouseMoved(int x, int y) { - dragManager.handleMove(x, y); - } } From 17b1a731b38f608dd11c3c47a1706e9af4afe056 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 22:16:20 +0200 Subject: [PATCH 027/661] add VMouseButton --- src/main/java/net/snackbag/vera/core/VMouseButton.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/core/VMouseButton.java diff --git a/src/main/java/net/snackbag/vera/core/VMouseButton.java b/src/main/java/net/snackbag/vera/core/VMouseButton.java new file mode 100644 index 00000000..8c9ef743 --- /dev/null +++ b/src/main/java/net/snackbag/vera/core/VMouseButton.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.core; + +public enum VMouseButton { + LEFT, + MIDDLE, + RIGHT +} From 41355881c0bd329f94606ae0f91062025c8ce2c8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 22:27:07 +0200 Subject: [PATCH 028/661] treeset -> linkedhashset --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index e2eff72e..4364dbcc 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -31,7 +31,7 @@ public abstract class VWidget> { private final HashMap> eventExecutors; private final List> visibilityConditions; - public final SortedSet classes = new TreeSet<>(); + public final LinkedHashSet classes = new LinkedHashSet<>(); public VWidget(int x, int y, int width, int height, VeraApp app) { this.x = x; From 2afc74ab81960455861c2024c48934d258097e7d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 22:28:33 +0200 Subject: [PATCH 029/661] add direction --- .../java/net/snackbag/vera/event/VMouseDragEvent.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java b/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java index 2fa90fba..c2a32bd7 100644 --- a/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java +++ b/src/main/java/net/snackbag/vera/event/VMouseDragEvent.java @@ -3,6 +3,13 @@ public interface VMouseDragEvent { void run(Context ctx); - record Context(int startX, int startY, int currentX, int currentY, int moveX, int moveY) { + record Context(int startX, int startY, int currentX, int currentY, int moveX, int moveY, Direction direction) { + } + + enum Direction { + UP, + DOWN, + LEFT, + RIGHT } } From e17eb79d671bd1c68955211f01b8a0393c108776 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 23:16:17 +0200 Subject: [PATCH 030/661] draw context is now initialized easier --- .../snackbag/mcvera/MinecraftVeraClient.java | 1 - .../mcvera/mixin/DrawContextMixin.java | 22 +++++++++++++++++++ src/main/resources/mcvera.mixins.json | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/snackbag/mcvera/mixin/DrawContextMixin.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java index 67f02eeb..1f434bd1 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java @@ -26,7 +26,6 @@ public void onInitializeClient() { TestHandler.impl(false); HudRenderCallback.EVENT.register((context, tickDelta) -> { - MCVeraRenderer.drawContext = context; MCVeraRenderer renderer = MCVeraRenderer.getInstance(); for (VeraApp app : MCVeraData.visibleApplications) { diff --git a/src/main/java/net/snackbag/mcvera/mixin/DrawContextMixin.java b/src/main/java/net/snackbag/mcvera/mixin/DrawContextMixin.java new file mode 100644 index 00000000..63bb2476 --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/mixin/DrawContextMixin.java @@ -0,0 +1,22 @@ +package net.snackbag.mcvera.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.snackbag.mcvera.impl.MCVeraRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(DrawContext.class) +@Environment(EnvType.CLIENT) +public abstract class DrawContextMixin { + @Inject(at = @At("TAIL"), method = "(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;)V") + private void snackbag$updateVeraDrawContext(MinecraftClient client, MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, CallbackInfo ci) { + MCVeraRenderer.drawContext = (DrawContext) ((Object) this); + } +} diff --git a/src/main/resources/mcvera.mixins.json b/src/main/resources/mcvera.mixins.json index 79f4f793..4ccca61e 100644 --- a/src/main/resources/mcvera.mixins.json +++ b/src/main/resources/mcvera.mixins.json @@ -10,6 +10,7 @@ "defaultRequire": 1 }, "client": [ + "DrawContextMixin", "KeyboardMixin", "MouseMixin", "ParentElementMixin", From befd8adcb5797c9bfa2534efbe4f7acafa47ff04 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 23:19:56 +0200 Subject: [PATCH 031/661] add flags --- .../net/snackbag/vera/flag/VWindowFlag.java | 5 +++ .../vera/flag/VWindowPositioningFlag.java | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/flag/VWindowFlag.java create mode 100644 src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java diff --git a/src/main/java/net/snackbag/vera/flag/VWindowFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowFlag.java new file mode 100644 index 00000000..3ab843d8 --- /dev/null +++ b/src/main/java/net/snackbag/vera/flag/VWindowFlag.java @@ -0,0 +1,5 @@ +package net.snackbag.vera.flag; + +public enum VWindowFlag { + DEBUG +} diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java new file mode 100644 index 00000000..41151f15 --- /dev/null +++ b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java @@ -0,0 +1,38 @@ +package net.snackbag.vera.flag; + +public enum VWindowPositioningFlag { + /** + * Renders under the HUD + */ + UNDER_HUD, + + /** + * Renders on the same level as the HUD + */ + HUD, + + /** + * Renders over the HUD and under normal GUI + */ + HUD_OVERLAY, + + /** + * Renders on the same layer as the GUI + */ + GUI, + + /** + * Renders over the GUI but under any other render events + */ + GUI_OVERLAY, + + /** + * Renders over all other render events + */ + SCREEN, + + /** + * Should only be used when wanting to 100% override something + */ + TOP +} From e63d39abd635f760e870c6941ed980072b572997 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 23:22:46 +0200 Subject: [PATCH 032/661] note default --- src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java index 41151f15..235c0df9 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java @@ -27,6 +27,7 @@ public enum VWindowPositioningFlag { GUI_OVERLAY, /** + * (default)
* Renders over all other render events */ SCREEN, From bcc5be789bb650e96a6a7583a7a0e18766ac1f33 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 23:38:21 +0200 Subject: [PATCH 033/661] apply window flag --- .../java/net/snackbag/mcvera/MCVeraData.java | 16 ++++++------ .../java/net/snackbag/vera/core/VeraApp.java | 26 +++++++++++++++---- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index 663b6299..ce8b0a42 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -1,17 +1,17 @@ package net.snackbag.mcvera; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowPositioningFlag; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class MCVeraData { - public static Set applications = new HashSet<>(); - public static Set visibleApplications = new HashSet<>(); + public static LinkedHashSet applications = new LinkedHashSet<>(); + public static HashMap> visibleApplications = new HashMap<>(); + + public static int appsWithMouseRequired = 0; + public static final Set debugApps = new HashSet<>(); + public static final List pressedKeys = new ArrayList<>(); public static List previousPressedKeys = new ArrayList<>(); - public static final Set debugApps = new HashSet<>(); - public static int appsWithMouseRequired = 0; } diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 730229f0..52253d0a 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -1,18 +1,16 @@ package net.snackbag.vera.core; import net.minecraft.client.MinecraftClient; +import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; -import net.snackbag.vera.util.DraggingManager; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; public abstract class VeraApp { @@ -32,6 +30,7 @@ public abstract class VeraApp { private boolean visible; private @Nullable VWidget focusedWidget; + private VWindowPositioningFlag positioning; public VeraApp() { this(true); @@ -53,6 +52,7 @@ public VeraApp(boolean mouseRequired) { this.y = 0; this.visible = false; + setPositioning(VWindowPositioningFlag.SCREEN); } public void setCursorVisible(boolean cursorVisible) { @@ -280,6 +280,22 @@ public void setCursorShape(VCursorShape cursorShape) { ); } + public VWindowPositioningFlag getPositioning() { + return positioning; + } + + public void setPositioning(VWindowPositioningFlag positioning) { + if (MCVeraData.visibleApplications.getOrDefault(positioning, new LinkedHashSet<>()).contains(this)) + MCVeraData.visibleApplications.get(positioning).remove(this); + + this.positioning = positioning; + + if (!MCVeraData.visibleApplications.containsKey(positioning)) + MCVeraData.visibleApplications.put(positioning, new LinkedHashSet<>()); + + MCVeraData.visibleApplications.get(positioning).add(this); + } + public void keyPressed(int keyCode, int scanCode, int modifiers) { if (hasFocusedWidget()) getFocusedWidget().keyPressed(keyCode, scanCode, modifiers); } From c8cdc8c3d00ea86fa5bcccbba2b6e6cd3a54ac8a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Jun 2025 23:47:22 +0200 Subject: [PATCH 034/661] add comments and whatever --- .../java/net/snackbag/vera/core/VeraApp.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 52253d0a..1bbce476 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -285,15 +285,20 @@ public VWindowPositioningFlag getPositioning() { } public void setPositioning(VWindowPositioningFlag positioning) { - if (MCVeraData.visibleApplications.getOrDefault(positioning, new LinkedHashSet<>()).contains(this)) - MCVeraData.visibleApplications.get(positioning).remove(this); - - this.positioning = positioning; - + // make sure hashmaps exist + if (!MCVeraData.visibleApplications.containsKey(this.positioning)) + MCVeraData.visibleApplications.put(this.positioning, new LinkedHashSet<>()); if (!MCVeraData.visibleApplications.containsKey(positioning)) MCVeraData.visibleApplications.put(positioning, new LinkedHashSet<>()); - MCVeraData.visibleApplications.get(positioning).add(this); + // if visible, then we can also add the app itself + if (isVisible()) { + MCVeraData.visibleApplications.get(positioning).add(this); + } + + // doesn't matter if visible or not, we always remove it from its original + MCVeraData.visibleApplications.get(this.positioning).remove(this); + this.positioning = positioning; } public void keyPressed(int keyCode, int scanCode, int modifiers) { From 442114a6b37c39ccf475b6abfc84453a28ee40e7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 00:12:57 +0200 Subject: [PATCH 035/661] hierarchy anarchy --- .../java/net/snackbag/mcvera/MCVeraData.java | 1 + .../snackbag/mcvera/MinecraftVeraClient.java | 5 +--- .../snackbag/mcvera/impl/MCVeraProvider.java | 12 +++----- .../mcvera/mixin/MinecraftClientMixin.java | 6 ++-- src/main/java/net/snackbag/vera/Vera.java | 29 +++++++++++++++++++ .../java/net/snackbag/vera/core/VeraApp.java | 21 ++++++++++++++ 6 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index ce8b0a42..ba17563a 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -8,6 +8,7 @@ public class MCVeraData { public static LinkedHashSet applications = new LinkedHashSet<>(); public static HashMap> visibleApplications = new HashMap<>(); + public static List appHierarchy = new ArrayList<>(); public static int appsWithMouseRequired = 0; public static final Set debugApps = new HashSet<>(); diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java index 1f434bd1..5266c3a6 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java @@ -9,7 +9,6 @@ import net.minecraft.client.util.InputUtil; import net.minecraft.resource.Resource; import net.minecraft.util.Identifier; -import net.snackbag.mcvera.impl.MCVeraProvider; import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.mcvera.test.TestHandler; import net.snackbag.vera.Vera; @@ -39,9 +38,7 @@ public void onInitializeClient() { MCVeraData.previousPressedKeys = new ArrayList<>(MCVeraData.pressedKeys); String combination = makeCombination(client); - for (VeraApp app : MCVeraData.visibleApplications) { - app.handleShortcut(combination); - } + Vera.forVisibleAndAllowedApps(app -> app.handleShortcut(combination)); }); } diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index f448dd94..4adb1c68 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -28,7 +28,7 @@ public void handleAppInitialization(VeraApp app) { public void handleAppShow(VeraApp app) { if (app.isVisible()) return; - MCVeraData.visibleApplications.add(app); + MCVeraData.visibleApplications.get(app.getPositioning()).add(app); if (app.isMouseRequired()) MCVeraData.appsWithMouseRequired += 1; MinecraftClient client = MinecraftClient.getInstance(); client.send(app::update); @@ -44,7 +44,7 @@ public void handleAppHide(VeraApp app) { if (!app.isVisible()) return; if (app.isMouseRequired()) MCVeraData.appsWithMouseRequired -= 1; - MCVeraData.visibleApplications.remove(app); + MCVeraData.visibleApplications.get(app.getPositioning()).remove(app); MinecraftClient client = MinecraftClient.getInstance(); client.send(app::update); @@ -101,15 +101,11 @@ public int getMouseY() { } public void handleKeyPressed(int keyCode, int scanCode, int modifiers) { - for (VeraApp app : MCVeraData.visibleApplications) { - app.keyPressed(keyCode, scanCode, modifiers); - } + Vera.forVisibleAndAllowedApps(app -> app.keyPressed(keyCode, scanCode, modifiers)); } public void handleCharTyped(char chr, int modifiers) { - for (VeraApp app : MCVeraData.visibleApplications) { - app.charTyped(chr, modifiers); - } + Vera.forVisibleAndAllowedApps(app -> app.charTyped(chr, modifiers)); } public String getDefaultFontName() { diff --git a/src/main/java/net/snackbag/mcvera/mixin/MinecraftClientMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MinecraftClientMixin.java index 540b98e6..cbcbf65d 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MinecraftClientMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MinecraftClientMixin.java @@ -4,7 +4,7 @@ import net.minecraft.client.gui.screen.Screen; import net.snackbag.mcvera.MCVeraData; import net.snackbag.mcvera.screen.VeraVisibilityScreen; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.Vera; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -36,11 +36,11 @@ public abstract class MinecraftClientMixin { private void mcvera$handleResize(CallbackInfo ci) { MinecraftClient client = MinecraftClient.getInstance(); - for (VeraApp app : MCVeraData.visibleApplications) { + Vera.forAllVisibleApps(app -> { client.send(app::update); for (VWidget widget : app.getWidgets()) { client.send(widget::update); } - } + }); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index 4b5bfbc3..d9e8f9fa 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -5,6 +5,7 @@ import net.snackbag.mcvera.impl.MCVeraProvider; import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; @@ -12,6 +13,7 @@ import org.lwjgl.util.tinyfd.TinyFileDialogs; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -22,6 +24,33 @@ public class Vera { public static final String FONT_DEFAULT = provider.getDefaultFontName(); public static final String FONT_ARIAL = "minecraft:arial"; + public static void forVisibleAndAllowedApps(Consumer handler) { + final List handledApps = new ArrayList<>(); + if (!MCVeraData.appHierarchy.isEmpty()) { + VeraApp app = MCVeraData.appHierarchy.get(0); + + handledApps.add(app); + handler.accept(app); + } + + for (VWindowPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { + for (VeraApp app : MCVeraData.visibleApplications.get(flag)) { + if (handledApps.contains(app) || app.isRequiresHierarchy()) continue; + + handler.accept(app); + handledApps.add(app); + } + } + } + + public static void forAllVisibleApps(Consumer handler) { + for (VWindowPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { + for (VeraApp app : MCVeraData.visibleApplications.get(flag)) { + handler.accept(app); + } + } + } + public static void forHoveredWidget(int mouseX, int mouseY, Consumer> runnable, @Nullable Consumer ifEmpty) { for (VeraApp app : MCVeraData.visibleApplications) { List> hoveredWidgets = app.getHoveredWidgets(mouseX, mouseY); diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 1bbce476..433ec037 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -29,6 +29,7 @@ public abstract class VeraApp { private int height; private boolean visible; + private boolean requiresHierarchy; private @Nullable VWidget focusedWidget; private VWindowPositioningFlag positioning; @@ -159,6 +160,26 @@ public int getY() { return y; } + public void setRequiresHierarchy(boolean requires) { + if (MCVeraData.appHierarchy.contains(this) && !requires) { + MCVeraData.appHierarchy.remove(this); + } + + MCVeraData.appHierarchy.add(this); + this.requiresHierarchy = requires; + } + + public void moveToHierarchyTop() { + if (!requiresHierarchy) return; + + MCVeraData.appHierarchy.remove(this); + MCVeraData.appHierarchy.add(0, this); + } + + public boolean isRequiresHierarchy() { + return requiresHierarchy; + } + public abstract void init(); public List> getWidgets() { From b9699299e99c19c641c52cf9da67fb6438076aab Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 11:51:59 +0200 Subject: [PATCH 036/661] add BELOW_ALL --- .../java/net/snackbag/vera/flag/VWindowPositioningFlag.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java index 235c0df9..9e30cf1e 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java @@ -1,6 +1,11 @@ package net.snackbag.vera.flag; public enum VWindowPositioningFlag { + /** + * Renders below everything, also under the spyglass + */ + BELOW_ALL, + /** * Renders under the HUD */ From cdee9c4c26cb6d6360a572687e16f0a396a85e24 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 11:54:08 +0200 Subject: [PATCH 037/661] add BELOW_VIGNETTE --- .../net/snackbag/vera/flag/VWindowPositioningFlag.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java index 9e30cf1e..69bf93c7 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java @@ -1,10 +1,15 @@ package net.snackbag.vera.flag; public enum VWindowPositioningFlag { + /** + * Deepest render position, renders even under the vignette + */ + BELOW_VIGNETTE, + /** * Renders below everything, also under the spyglass */ - BELOW_ALL, + BELOW_OVERLAYS, /** * Renders under the HUD From b3f06c4a2646df7216ed8396f381a95ea33abed5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 11:57:23 +0200 Subject: [PATCH 038/661] remove legacy instancing --- .../java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index c72b978f..b7d79d90 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -16,17 +16,8 @@ import java.util.List; public class MCVeraRenderer { - private static MCVeraRenderer instance = null; public static DrawContext drawContext = null; - public static MCVeraRenderer getInstance() { - if (instance == null) { - instance = new MCVeraRenderer(); - } - - return instance; - } - public void drawRect(VeraApp app, int x, int y, int width, int height, double rotation, VColor color) { MatrixStack stack = drawContext.getMatrices(); stack.push(); From 13fc493503bdf8a4f6c8ff42b68d4f90ccaa1fc4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 11:57:29 +0200 Subject: [PATCH 039/661] fix division --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index b7d79d90..4b3bb1c8 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -28,7 +28,7 @@ public void drawRect(VeraApp app, int x, int y, int width, int height, double ro stack.translate(app.getX(), app.getY(), 0); stack.translate(centerX, centerY, 0); stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((float) rotation)); - stack.translate(-width / 2, -height / 2, 0); + stack.translate(-width / 2f, -height / 2f, 0); drawContext.fill(0, 0, width, height, color.toInt()); From 40b3c7f75913b61f9d81f425ae224aaa69ece16c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 11:57:36 +0200 Subject: [PATCH 040/661] remove old render event --- .../java/net/snackbag/mcvera/MinecraftVeraClient.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java index 5266c3a6..996cdf5d 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java @@ -24,14 +24,6 @@ public void onInitializeClient() { MinecraftVera.LOGGER.info("Loading client Vera implementation..."); TestHandler.impl(false); - HudRenderCallback.EVENT.register((context, tickDelta) -> { - MCVeraRenderer renderer = MCVeraRenderer.getInstance(); - - for (VeraApp app : MCVeraData.visibleApplications) { - renderer.renderApp(app); - } - }); - ClientTickEvents.END_CLIENT_TICK.register((client) -> { // only when changing if (MCVeraData.previousPressedKeys.equals(MCVeraData.pressedKeys)) return; From 919337ad2e5fff9d4dc3b770077a3419d302045d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 12:02:23 +0200 Subject: [PATCH 041/661] UNDER_HUD -> BELOW_HUD HUD_OVERLAY -> ABOVE_HUD GUI_OVERLAY -> ABOVE_GUI --- .../java/net/snackbag/vera/flag/VWindowPositioningFlag.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java index 69bf93c7..59810b2e 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java @@ -14,7 +14,7 @@ public enum VWindowPositioningFlag { /** * Renders under the HUD */ - UNDER_HUD, + BELOW_HUD, /** * Renders on the same level as the HUD @@ -24,7 +24,7 @@ public enum VWindowPositioningFlag { /** * Renders over the HUD and under normal GUI */ - HUD_OVERLAY, + ABOVE_HUD, /** * Renders on the same layer as the GUI @@ -34,7 +34,7 @@ public enum VWindowPositioningFlag { /** * Renders over the GUI but under any other render events */ - GUI_OVERLAY, + ABOVE_GUI, /** * (default)
From 24c238f07dc89a33d39f7528e97b674d0f14b549 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 14:34:25 +0200 Subject: [PATCH 042/661] add warning notice --- .../java/net/snackbag/vera/flag/VWindowPositioningFlag.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java index 59810b2e..0ddebd8b 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java @@ -28,6 +28,10 @@ public enum VWindowPositioningFlag { /** * Renders on the same layer as the GUI + *

+ * Watch out: if the UI doesn't require a mouse to exist, it will not render if no other app that + * does require a mouse is present. In case you 100% need it, it is recommended to either use {@link #ABOVE_HUD} or + * {@link #ABOVE_GUI} */ GUI, From cf14d37efcf1f5f11583c7cde3f8fe3bd9054f7a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 14:34:32 +0200 Subject: [PATCH 043/661] render first few apps --- .../snackbag/mcvera/mixin/InGameHudMixin.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java diff --git a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java new file mode 100644 index 00000000..afc7df61 --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java @@ -0,0 +1,62 @@ +package net.snackbag.mcvera.mixin; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.hud.InGameHud; +import net.snackbag.mcvera.MCVeraData; +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowPositioningFlag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.LinkedHashSet; + +@Mixin(InGameHud.class) +public abstract class InGameHudMixin { + @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;enableBlend()V", shift = At.Shift.AFTER, ordinal = 0), method = "render") + private void mcvera$renderBelowVignette(DrawContext context, float tickDelta, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.BELOW_VIGNETTE, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + + RenderSystem.enableBlend(); + } + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getLastFrameDuration()F"), method = "render") + private void mcvera$renderBelowOverlays(DrawContext context, float tickDelta, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.BELOW_OVERLAYS, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + + RenderSystem.enableBlend(); + } + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;getCurrentGameMode()Lnet/minecraft/world/GameMode;", ordinal = 0, shift = At.Shift.BEFORE), method = "render") + private void mcvera$renderBelowHud(DrawContext context, float tickDelta, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.BELOW_HUD, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + + RenderSystem.enableBlend(); + } + + @Inject(at = @At(value = "TAIL"), method = "renderHotbar") + private void mcvera$renderHud(float tickDelta, DrawContext context, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.HUD, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + + RenderSystem.enableBlend(); + } +} From cf28d03466373ea55b179244a92f18d267c0be8f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 15:06:27 +0200 Subject: [PATCH 044/661] only modify blend if needed --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 4b3bb1c8..e3c9dcf9 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -12,6 +12,7 @@ import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.widget.VWidget; +import org.lwjgl.opengl.GL11; import java.util.List; @@ -57,8 +58,10 @@ public void drawImage(VeraApp app, int x, int y, int width, int height, double r } public void renderApp(VeraApp app) { + boolean blendEnabled = GL11.glIsEnabled(GL11.GL_BLEND); + List> widgets = app.getWidgets(); - RenderSystem.enableBlend(); + if (!blendEnabled) RenderSystem.enableBlend(); app.render(); List> hoveredWidgets = app.getHoveredWidgets(); @@ -71,6 +74,6 @@ public void renderApp(VeraApp app) { } app.renderAfterWidgets(); - RenderSystem.disableBlend(); + if (!blendEnabled) RenderSystem.disableBlend(); } } From 942c5f27fd4386647b1011e5293f0427d817960a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 15:06:52 +0200 Subject: [PATCH 045/661] remove unnecessary enable calls --- .../mcvera/mixin/GameRendererMixin.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java diff --git a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java new file mode 100644 index 00000000..622f7856 --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java @@ -0,0 +1,48 @@ +package net.snackbag.mcvera.mixin; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.render.GameRenderer; +import net.snackbag.mcvera.MCVeraData; +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowPositioningFlag; +import org.lwjgl.opengl.GL11; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.LinkedHashSet; + +@Mixin(GameRenderer.class) +public abstract class GameRendererMixin { + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getOverlay()Lnet/minecraft/client/gui/screen/Overlay;", ordinal = 0, shift = At.Shift.BEFORE)) + private void mcvera$renderAboveHud(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.ABOVE_HUD, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + + GL11.glIsEnabled(GL11.GL_BLEND); + } + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V")) + private void mcvera$renderOnGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.GUI, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + } + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V")) + private void mcvera$renderAboveGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.ABOVE_GUI, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + } +} From 474ee5a979481efd4992712292315616cb4c7c6e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 15:12:02 +0200 Subject: [PATCH 046/661] remove unnecessary enable calls --- .../java/net/snackbag/mcvera/mixin/InGameHudMixin.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java index afc7df61..70a934ff 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java @@ -23,8 +23,6 @@ public abstract class InGameHudMixin { for (VeraApp app : apps) { Vera.renderer.renderApp(app); } - - RenderSystem.enableBlend(); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getLastFrameDuration()F"), method = "render") @@ -34,8 +32,6 @@ public abstract class InGameHudMixin { for (VeraApp app : apps) { Vera.renderer.renderApp(app); } - - RenderSystem.enableBlend(); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;getCurrentGameMode()Lnet/minecraft/world/GameMode;", ordinal = 0, shift = At.Shift.BEFORE), method = "render") @@ -45,8 +41,6 @@ public abstract class InGameHudMixin { for (VeraApp app : apps) { Vera.renderer.renderApp(app); } - - RenderSystem.enableBlend(); } @Inject(at = @At(value = "TAIL"), method = "renderHotbar") @@ -56,7 +50,5 @@ public abstract class InGameHudMixin { for (VeraApp app : apps) { Vera.renderer.renderApp(app); } - - RenderSystem.enableBlend(); } } From 51098ef1f397e527ada4986aae527455394f0936 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 15:12:08 +0200 Subject: [PATCH 047/661] register mixins --- src/main/resources/mcvera.mixins.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/mcvera.mixins.json b/src/main/resources/mcvera.mixins.json index 4ccca61e..46321a8d 100644 --- a/src/main/resources/mcvera.mixins.json +++ b/src/main/resources/mcvera.mixins.json @@ -11,6 +11,8 @@ }, "client": [ "DrawContextMixin", + "GameRendererMixin", + "InGameHudMixin", "KeyboardMixin", "MouseMixin", "ParentElementMixin", From ba27986250172e94ef973cd517d08aef16694fe5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 15:50:48 +0200 Subject: [PATCH 048/661] render screen and top --- .../mcvera/mixin/GameRendererMixin.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java index 622f7856..aeb13d41 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java @@ -37,7 +37,7 @@ public abstract class GameRendererMixin { } } - @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V")) + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.AFTER)) private void mcvera$renderAboveGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.ABOVE_GUI, new LinkedHashSet<>()); @@ -45,4 +45,19 @@ public abstract class GameRendererMixin { Vera.renderer.renderApp(app); } } + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/toast/ToastManager;draw(Lnet/minecraft/client/gui/DrawContext;)V", shift = At.Shift.AFTER)) + private void mcvera$renderScreenAndTop(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.SCREEN, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + + apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.TOP, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + } } From 23f585eb86ba963c7af16bc27a29a69cfee624cc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 15:54:35 +0200 Subject: [PATCH 049/661] make method to render apps of flag --- .../snackbag/mcvera/impl/MCVeraRenderer.java | 12 +++++++ .../mcvera/mixin/GameRendererMixin.java | 33 +++---------------- .../snackbag/mcvera/mixin/InGameHudMixin.java | 24 +++----------- 3 files changed, 21 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index e3c9dcf9..672d32a4 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -8,12 +8,16 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.math.RotationAxis; +import net.snackbag.mcvera.MCVeraData; +import net.snackbag.vera.Vera; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.widget.VWidget; import org.lwjgl.opengl.GL11; +import java.util.LinkedHashSet; import java.util.List; public class MCVeraRenderer { @@ -76,4 +80,12 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.disableBlend(); } + + public void renderApps(VWindowPositioningFlag flag) { + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); + + for (VeraApp app : apps) { + Vera.renderer.renderApp(app); + } + } } diff --git a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java index aeb13d41..1fa3ab0c 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java @@ -19,45 +19,22 @@ public abstract class GameRendererMixin { @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getOverlay()Lnet/minecraft/client/gui/screen/Overlay;", ordinal = 0, shift = At.Shift.BEFORE)) private void mcvera$renderAboveHud(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.ABOVE_HUD, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } - - GL11.glIsEnabled(GL11.GL_BLEND); + Vera.renderer.renderApps(VWindowPositioningFlag.ABOVE_HUD); } @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V")) private void mcvera$renderOnGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.GUI, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.GUI); } @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.AFTER)) private void mcvera$renderAboveGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.ABOVE_GUI, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.ABOVE_GUI); } @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/toast/ToastManager;draw(Lnet/minecraft/client/gui/DrawContext;)V", shift = At.Shift.AFTER)) private void mcvera$renderScreenAndTop(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.SCREEN, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } - - apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.TOP, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.SCREEN); + Vera.renderer.renderApps(VWindowPositioningFlag.TOP); } } diff --git a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java index 70a934ff..b0c8cdf1 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java @@ -18,37 +18,21 @@ public abstract class InGameHudMixin { @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;enableBlend()V", shift = At.Shift.AFTER, ordinal = 0), method = "render") private void mcvera$renderBelowVignette(DrawContext context, float tickDelta, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.BELOW_VIGNETTE, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_VIGNETTE); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getLastFrameDuration()F"), method = "render") private void mcvera$renderBelowOverlays(DrawContext context, float tickDelta, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.BELOW_OVERLAYS, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_OVERLAYS); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;getCurrentGameMode()Lnet/minecraft/world/GameMode;", ordinal = 0, shift = At.Shift.BEFORE), method = "render") private void mcvera$renderBelowHud(DrawContext context, float tickDelta, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.BELOW_HUD, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_HUD); } @Inject(at = @At(value = "TAIL"), method = "renderHotbar") private void mcvera$renderHud(float tickDelta, DrawContext context, CallbackInfo ci) { - LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(VWindowPositioningFlag.HUD, new LinkedHashSet<>()); - - for (VeraApp app : apps) { - Vera.renderer.renderApp(app); - } + Vera.renderer.renderApps(VWindowPositioningFlag.HUD); } } From b544d237d7d064289b20a5d4b45692790232cc67 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 21:13:19 +0200 Subject: [PATCH 050/661] isTopHierarchy and getTopHierarchyApp --- src/main/java/net/snackbag/vera/Vera.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index d9e8f9fa..6fbe4bf1 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -105,4 +105,12 @@ public static int getMouseY() { return path; } } + + public static @Nullable VeraApp getTopHierarchyApp() { + return MCVeraData.appHierarchy.isEmpty() ? null : MCVeraData.appHierarchy.get(0); + } + + public static boolean isTopHierarchy(VeraApp app) { + return getTopHierarchyApp() == app; + } } From fcab55a10635ebd228bf2712323d11db02e8a1f8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 21:55:20 +0200 Subject: [PATCH 051/661] render hierarchy later --- .../java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 672d32a4..b362aa20 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -17,6 +17,7 @@ import net.snackbag.vera.widget.VWidget; import org.lwjgl.opengl.GL11; +import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -83,8 +84,18 @@ public void renderApp(VeraApp app) { public void renderApps(VWindowPositioningFlag flag) { LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); + List hierarchicApps = new ArrayList<>(); for (VeraApp app : apps) { + if (app.isRequiresHierarchy()) { + hierarchicApps.add(app); + continue; + } + + Vera.renderer.renderApp(app); + } + + for (VeraApp app : hierarchicApps) { Vera.renderer.renderApp(app); } } From f6132c1a8572c717aaaffd8381a05d2ab947cbb4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 14 Jun 2025 22:28:44 +0200 Subject: [PATCH 052/661] remove old methods --- src/main/java/net/snackbag/vera/Vera.java | 25 ----------------------- 1 file changed, 25 deletions(-) diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index 6fbe4bf1..7f31b7fc 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -51,31 +51,6 @@ public static void forAllVisibleApps(Consumer handler) { } } - public static void forHoveredWidget(int mouseX, int mouseY, Consumer> runnable, @Nullable Consumer ifEmpty) { - for (VeraApp app : MCVeraData.visibleApplications) { - List> hoveredWidgets = app.getHoveredWidgets(mouseX, mouseY); - if (hoveredWidgets.isEmpty()) { - if (ifEmpty != null) ifEmpty.accept(app); - return; - } - - for (VWidget widget : hoveredWidgets) { - if (widget.visibilityConditionsPassed()) runnable.accept(widget); - } - } - } - - public static void forHoveredWidget(int mouseX, int mouseY, Consumer> runnable) { - forHoveredWidget(mouseX, mouseY, runnable, null); - } - - public static void forHoveredWidgetIfEmpty(int mouseX, int mouseY, Consumer runnable) { - for (VeraApp app : MCVeraData.visibleApplications) { - List> hoveredWidgets = app.getHoveredWidgets(mouseX, mouseY); - if (hoveredWidgets.isEmpty()) runnable.accept(app); - } - } - public static int getMouseX() { return (int) (MinecraftClient.getInstance().mouse.getX() / MinecraftClient.getInstance().getWindow().getScaleFactor()); } From f8511dae02f77c5dd0f0c4efa5df1e322aa83880 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:20:38 +0200 Subject: [PATCH 053/661] geometry class --- .../java/net/snackbag/vera/util/Geometry.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/util/Geometry.java diff --git a/src/main/java/net/snackbag/vera/util/Geometry.java b/src/main/java/net/snackbag/vera/util/Geometry.java new file mode 100644 index 00000000..3e66b01c --- /dev/null +++ b/src/main/java/net/snackbag/vera/util/Geometry.java @@ -0,0 +1,20 @@ +package net.snackbag.vera.util; + +public class Geometry { + /** + * Method to check whether a coordinate in within the boundaries of a box + * + * @param x check x coord + * @param y check y coord + * @param wx box x coord + * @param wy box y coord + * @param wwidth box width + * @param wheight box height + * + * @return whether param x and y are in the specified box boundaries + */ + public static boolean isInBox(int x, int y, int wx, int wy, int wwidth, int wheight) { + return x >= wx && x <= wx + wwidth && + y >= wy && y <= wy + wheight; + } +} From 447c99453604488bde7a6d9fbe368befcb97eac0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:20:56 +0200 Subject: [PATCH 054/661] use Geometry instead of manual --- src/main/java/net/snackbag/vera/core/VeraApp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 433ec037..63cfd7ae 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -6,6 +6,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -256,8 +257,7 @@ private boolean isMouseOverWidget(VWidget widget, int mouseX, int mouseY) { int widgetY = widget.getHitboxY() + y; int widgetWidth = widget.getHitboxWidth(); int widgetHeight = widget.getHitboxHeight(); - return mouseX >= widgetX && mouseX <= widgetX + widgetWidth && - mouseY >= widgetY && mouseY <= widgetY + widgetHeight; + return Geometry.isInBox(mouseX, mouseY, widgetX, widgetY, widgetWidth, widgetHeight); } public boolean isMouseOverApp(int mouseX, int mouseY) { From c466ab101a40bca9a7e6822a48eef4be1d31a2ca Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:22:15 +0200 Subject: [PATCH 055/661] add isMouseOverThis method --- src/main/java/net/snackbag/vera/core/VeraApp.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 63cfd7ae..bf8f076d 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -260,6 +260,12 @@ private boolean isMouseOverWidget(VWidget widget, int mouseX, int mouseY) { return Geometry.isInBox(mouseX, mouseY, widgetX, widgetY, widgetWidth, widgetHeight); } + public boolean isMouseOverThis(int mouseX, int mouseY) { + if (!isVisible()) return false; + + return Geometry.isInBox(mouseX, mouseY, x, y, width, height); + } + public boolean isMouseOverApp(int mouseX, int mouseY) { if (!isVisible()) return false; return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height; From 74248ddceba7bc190145453d347d1a55ee8e617f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:30:17 +0200 Subject: [PATCH 056/661] new hovered widget check --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index b362aa20..e58843f7 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -69,9 +69,9 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.enableBlend(); app.render(); - List> hoveredWidgets = app.getHoveredWidgets(); + VWidget hoveredWidget = app.getHoveredWidget(Vera.getMouseX(), Vera.getMouseY()); for (VWidget widget : widgets) { - widget.setHovered(hoveredWidgets.contains(widget)); + widget.setHovered(widget == hoveredWidget); if (widget.visibilityConditionsPassed()) { widget.render(); widget.renderBorder(); From 811a9ceecb4945e0944c38f4c1de20b8c62566ec Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:30:28 +0200 Subject: [PATCH 057/661] asTopHierarchy method --- src/main/java/net/snackbag/mcvera/MCVeraData.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index ba17563a..e499af75 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -2,8 +2,10 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VWindowPositioningFlag; +import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.function.Consumer; public class MCVeraData { public static LinkedHashSet applications = new LinkedHashSet<>(); @@ -15,4 +17,17 @@ public class MCVeraData { public static final List pressedKeys = new ArrayList<>(); public static List previousPressedKeys = new ArrayList<>(); + + /** + * Executes a method as the top hierarchy app. If there is no top app it won't execute the specified code. + * + * @param runnable code to execute + * @return whether something has been executed + */ + public static boolean asTopHierarchy(@NotNull Consumer runnable) { + if (appHierarchy.isEmpty()) return false; + + runnable.accept(appHierarchy.get(0)); + return true; + } } From c4446a4264a640fceb70ed0c913c453e6f87ca64 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:30:47 +0200 Subject: [PATCH 058/661] getHoveredWidgets -> getHoveredWidget --- src/main/java/net/snackbag/vera/core/VeraApp.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index bf8f076d..8c095af0 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -236,18 +236,14 @@ public List getShortcuts() { return List.copyOf(shortcuts.values()); } - public List> getHoveredWidgets() { - return getHoveredWidgets(Vera.provider.getMouseX(), Vera.provider.getMouseY()); - } - - public List> getHoveredWidgets(int mouseX, int mouseY) { + public @Nullable VWidget getHoveredWidget(int mouseX, int mouseY) { int mx = mouseX - x; int my = mouseY - y; return getWidgets().parallelStream() .filter(widget -> isMouseOverWidget(widget, mx, my)) .filter(VWidget::visibilityConditionsPassed) - .collect(Collectors.toList()); + .findFirst().orElse(null); } private boolean isMouseOverWidget(VWidget widget, int mouseX, int mouseY) { From 90ecb3c860ee07f83d66463bee62b3354feb9994 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:31:59 +0200 Subject: [PATCH 059/661] stop hover method spam --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index e58843f7..d4be24ad 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -71,7 +71,9 @@ public void renderApp(VeraApp app) { app.render(); VWidget hoveredWidget = app.getHoveredWidget(Vera.getMouseX(), Vera.getMouseY()); for (VWidget widget : widgets) { - widget.setHovered(widget == hoveredWidget); + if (widget != hoveredWidget && widget.isHovered()) widget.setHovered(false); + else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); + if (widget.visibilityConditionsPassed()) { widget.render(); widget.renderBorder(); From 55c92f07d5478e0b8aeca993667f34b426222287 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:40:10 +0200 Subject: [PATCH 060/661] new handleMouseClick logic --- .../mcvera/mixin/ParentElementMixin.java | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index b47fbf98..08eb41f8 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -1,8 +1,13 @@ package net.snackbag.mcvera.mixin; import net.minecraft.client.gui.ParentElement; +import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -10,15 +15,41 @@ @Mixin(ParentElement.class) public interface ParentElementMixin { @Inject(method = "mouseClicked", at = @At("HEAD")) - private void mcvera$handleMouseClick(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { - Vera.forHoveredWidget((int) mouseX, (int) mouseY, (widget) -> { - switch (button) { - case 0: widget.fireEvent("left-click"); break; - case 1: widget.fireEvent("right-click"); break; - case 2: widget.fireEvent("middle-click"); break; - default: throw new IllegalStateException("Invalid button type: " + button); + private void mcvera$handleMouseClick(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { + int mouseX = (int) mouseXRaw; + int mouseY = (int) mouseYRaw; + + for (VeraApp app : MCVeraData.appHierarchy) { + if (app.isMouseOverApp(mouseX, mouseY)) { + app.moveToHierarchyTop(); + break; } - }, (app) -> app.setFocusedWidget(null)); + } + + MCVeraData.asTopHierarchy(app -> { + if (!app.isMouseOverThis(mouseX, mouseY)) return; + handleClickEvents(app.getHoveredWidget(mouseX, mouseY), button); + }); + + Vera.forAllVisibleApps((app) -> { + if (app.isRequiresHierarchy()) return; + + VWidget hoveredWidget = app.getHoveredWidget(mouseX, mouseY); + if (hoveredWidget != null) handleClickEvents(hoveredWidget, button); + else app.setFocusedWidget(null); + }); + } + + @Unique + private void handleClickEvents(@Nullable VWidget widget, int button) { + if (widget == null) return; + + switch (button) { + case 0 -> widget.fireEvent("left-click"); + case 1 -> widget.fireEvent("right-click"); + case 2 -> widget.fireEvent("middle-click"); + default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); + } } @Inject(method = "mouseReleased", at = @At("HEAD")) From 83cc2a01b47437d26dc2674a7e8c8aedd5b93bf0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:40:19 +0200 Subject: [PATCH 061/661] add getTopHierarchy method --- src/main/java/net/snackbag/mcvera/MCVeraData.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index e499af75..5d0adef7 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -3,6 +3,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Consumer; @@ -30,4 +31,8 @@ public static boolean asTopHierarchy(@NotNull Consumer runnable) { runnable.accept(appHierarchy.get(0)); return true; } + + public static @Nullable VeraApp getTopHierarchy() { + return appHierarchy.isEmpty() ? null : appHierarchy.get(0); + } } From 9df1f38bed793545a86ae064a1d0351efd4ce922 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:44:21 +0200 Subject: [PATCH 062/661] only move to top when needed and don't do funny stuff --- .../net/snackbag/mcvera/mixin/ParentElementMixin.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 08eb41f8..dd60a837 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -18,16 +18,22 @@ public interface ParentElementMixin { private void mcvera$handleMouseClick(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { int mouseX = (int) mouseXRaw; int mouseY = (int) mouseYRaw; + boolean justChanged = false; + + VeraApp top = MCVeraData.getTopHierarchy(); for (VeraApp app : MCVeraData.appHierarchy) { - if (app.isMouseOverApp(mouseX, mouseY)) { + if (app.isMouseOverApp(mouseX, mouseY) && top != app) { app.moveToHierarchyTop(); + justChanged = true; break; } } + boolean finalJustChanged = justChanged; // weird java shit MCVeraData.asTopHierarchy(app -> { if (!app.isMouseOverThis(mouseX, mouseY)) return; + if (!finalJustChanged) return; handleClickEvents(app.getHoveredWidget(mouseX, mouseY), button); }); From 894b790dd25a2f4b7845ceb20925e07578aa4165 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:50:32 +0200 Subject: [PATCH 063/661] add handleReleaseEvents --- .../snackbag/mcvera/mixin/ParentElementMixin.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index dd60a837..2929cee5 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -69,6 +69,18 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { }); } + @Unique + private void handleReleaseEvents(@Nullable VWidget widget, int button) { + if (widget == null) return; + + switch (button) { + case 0 -> widget.fireEvent("left-click-release"); + case 1 -> widget.fireEvent("right-click-release"); + case 2 -> widget.fireEvent("middle-click-release"); + default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); + } + } + @Inject(method = "mouseScrolled", at = @At("HEAD")) private void mcvera$handleMouseScroll(double mouseX, double mouseY, double amount, CallbackInfoReturnable cir) { Vera.forHoveredWidget((int) mouseX, (int) mouseY, (widget) -> { From 140433afae1da9ec83bcbf5afbfa9fd463b63008 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:52:58 +0200 Subject: [PATCH 064/661] new mouseReleased logic --- .../mcvera/mixin/ParentElementMixin.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 2929cee5..16a29ccc 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -59,13 +59,17 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { } @Inject(method = "mouseReleased", at = @At("HEAD")) - private void mcvera$handleMouseRelease(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { - Vera.forHoveredWidget((int) mouseX, (int) mouseY, (widget) -> { - switch (button) { - case 0: widget.fireEvent("left-click-release"); break; - case 1: widget.fireEvent("right-click-release"); break; - case 2: widget.fireEvent("middle-click-release"); break; - } + private void mcvera$handleMouseRelease(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { + int mouseX = (int) mouseXRaw; + int mouseY = (int) mouseYRaw; + + VeraApp top = MCVeraData.getTopHierarchy(); + if (top != null && top.isMouseOverThis(mouseX, mouseY)) + handleReleaseEvents(top.getHoveredWidget(mouseX, mouseY), button); + + Vera.forAllVisibleApps(app -> { + if (app.isRequiresHierarchy()) return; + handleReleaseEvents(app.getHoveredWidget(mouseX, mouseY), button); }); } From 6145589b0cbb431c3d79d147e93117bb5d35e051 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:56:42 +0200 Subject: [PATCH 065/661] simpler hierarchy logic approach --- .../java/net/snackbag/mcvera/mixin/ParentElementMixin.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 16a29ccc..6ccf06dc 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -63,10 +63,7 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { int mouseX = (int) mouseXRaw; int mouseY = (int) mouseYRaw; - VeraApp top = MCVeraData.getTopHierarchy(); - if (top != null && top.isMouseOverThis(mouseX, mouseY)) - handleReleaseEvents(top.getHoveredWidget(mouseX, mouseY), button); - + MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getHoveredWidget(mouseX, mouseY), button)); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; handleReleaseEvents(app.getHoveredWidget(mouseX, mouseY), button); From 88ccb76540b82bfc8dec9973fafe8bd5737d3af0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 00:56:48 +0200 Subject: [PATCH 066/661] new scroll logic --- .../mcvera/mixin/ParentElementMixin.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 6ccf06dc..56b96b9f 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -83,9 +83,20 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { } @Inject(method = "mouseScrolled", at = @At("HEAD")) - private void mcvera$handleMouseScroll(double mouseX, double mouseY, double amount, CallbackInfoReturnable cir) { - Vera.forHoveredWidget((int) mouseX, (int) mouseY, (widget) -> { - widget.fireEvent("mouse-scroll", (int) mouseX, (int) mouseY, amount); + private void mcvera$handleMouseScroll(double mouseXRaw, double mouseYRaw, double amount, CallbackInfoReturnable cir) { + int mouseX = (int) mouseXRaw; + int mouseY = (int) mouseYRaw; + + MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getHoveredWidget(mouseX, mouseY), mouseX, mouseY, amount)); + Vera.forAllVisibleApps(app -> { + if (app.isRequiresHierarchy()) return; + handleScrollEvents(app.getHoveredWidget(mouseX, mouseY), mouseX, mouseY, amount); }); } + + @Unique + private void handleScrollEvents(@Nullable VWidget widget, int x, int y, double amount) { + if (widget == null) return; + widget.fireEvent("mouse-scroll", x, y, amount); + } } From b99935e817229bc312de5af140d753178affd280 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 01:01:13 +0200 Subject: [PATCH 067/661] isMouseOverThis -> isPointOverThis isMouseOverWidget -> isPointOverWidget getHoveredWidget -> getTopWidgetAt --- .../net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- .../mcvera/mixin/ParentElementMixin.java | 16 ++++++++-------- .../java/net/snackbag/vera/core/VeraApp.java | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index d4be24ad..3c72d454 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -69,7 +69,7 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.enableBlend(); app.render(); - VWidget hoveredWidget = app.getHoveredWidget(Vera.getMouseX(), Vera.getMouseY()); + VWidget hoveredWidget = app.getTopWidgetAt(Vera.getMouseX(), Vera.getMouseY()); for (VWidget widget : widgets) { if (widget != hoveredWidget && widget.isHovered()) widget.setHovered(false); else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 56b96b9f..d686dc2e 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -23,7 +23,7 @@ public interface ParentElementMixin { VeraApp top = MCVeraData.getTopHierarchy(); for (VeraApp app : MCVeraData.appHierarchy) { - if (app.isMouseOverApp(mouseX, mouseY) && top != app) { + if (app.isPointOverThis(mouseX, mouseY) && top != app) { app.moveToHierarchyTop(); justChanged = true; break; @@ -32,15 +32,15 @@ public interface ParentElementMixin { boolean finalJustChanged = justChanged; // weird java shit MCVeraData.asTopHierarchy(app -> { - if (!app.isMouseOverThis(mouseX, mouseY)) return; + if (!app.isPointOverThis(mouseX, mouseY)) return; if (!finalJustChanged) return; - handleClickEvents(app.getHoveredWidget(mouseX, mouseY), button); + handleClickEvents(app.getTopWidgetAt(mouseX, mouseY), button); }); Vera.forAllVisibleApps((app) -> { if (app.isRequiresHierarchy()) return; - VWidget hoveredWidget = app.getHoveredWidget(mouseX, mouseY); + VWidget hoveredWidget = app.getTopWidgetAt(mouseX, mouseY); if (hoveredWidget != null) handleClickEvents(hoveredWidget, button); else app.setFocusedWidget(null); }); @@ -63,10 +63,10 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { int mouseX = (int) mouseXRaw; int mouseY = (int) mouseYRaw; - MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getHoveredWidget(mouseX, mouseY), button)); + MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), button)); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; - handleReleaseEvents(app.getHoveredWidget(mouseX, mouseY), button); + handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), button); }); } @@ -87,10 +87,10 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { int mouseX = (int) mouseXRaw; int mouseY = (int) mouseYRaw; - MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getHoveredWidget(mouseX, mouseY), mouseX, mouseY, amount)); + MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount)); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; - handleScrollEvents(app.getHoveredWidget(mouseX, mouseY), mouseX, mouseY, amount); + handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount); }); } diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 8c095af0..a948a46c 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -236,30 +236,30 @@ public List getShortcuts() { return List.copyOf(shortcuts.values()); } - public @Nullable VWidget getHoveredWidget(int mouseX, int mouseY) { - int mx = mouseX - x; - int my = mouseY - y; + public @Nullable VWidget getTopWidgetAt(int px, int py) { + int mx = px - x; + int my = py - y; return getWidgets().parallelStream() - .filter(widget -> isMouseOverWidget(widget, mx, my)) + .filter(widget -> isPointOverWidget(widget, mx, my)) .filter(VWidget::visibilityConditionsPassed) .findFirst().orElse(null); } - private boolean isMouseOverWidget(VWidget widget, int mouseX, int mouseY) { + private boolean isPointOverWidget(VWidget widget, int px, int py) { if (!widget.visibilityConditionsPassed()) return false; int widgetX = widget.getHitboxX() + x; int widgetY = widget.getHitboxY() + y; int widgetWidth = widget.getHitboxWidth(); int widgetHeight = widget.getHitboxHeight(); - return Geometry.isInBox(mouseX, mouseY, widgetX, widgetY, widgetWidth, widgetHeight); + return Geometry.isInBox(px, py, widgetX, widgetY, widgetWidth, widgetHeight); } - public boolean isMouseOverThis(int mouseX, int mouseY) { + public boolean isPointOverThis(int px, int py) { if (!isVisible()) return false; - return Geometry.isInBox(mouseX, mouseY, x, y, width, height); + return Geometry.isInBox(px, py, x, y, width, height); } public boolean isMouseOverApp(int mouseX, int mouseY) { From 419e603c0283c12f85dac013d021deb5ed48d838 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 01:01:20 +0200 Subject: [PATCH 068/661] clean imports --- src/main/java/net/snackbag/vera/core/VeraApp.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index a948a46c..db022e6d 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -12,7 +12,6 @@ import org.lwjgl.glfw.GLFW; import java.util.*; -import java.util.stream.Collectors; public abstract class VeraApp { public final VStyleSheet styleSheet = new VStyleSheet(); From 48898a4b5e5e39912858d001b75778b9f99fbb62 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 01:01:25 +0200 Subject: [PATCH 069/661] remove old method --- src/main/java/net/snackbag/vera/core/VeraApp.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index db022e6d..1d9d1f45 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -261,11 +261,6 @@ public boolean isPointOverThis(int px, int py) { return Geometry.isInBox(px, py, x, y, width, height); } - public boolean isMouseOverApp(int mouseX, int mouseY) { - if (!isVisible()) return false; - return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height; - } - public void setFocusedWidget(@Nullable VWidget widget) { if (this.focusedWidget != widget) { VWidget oldWidget = this.focusedWidget; From 7472bb6a80b1cece98bf515cbe6985f0f1e82aa6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 01:01:38 +0200 Subject: [PATCH 070/661] SortedSet -> LinkedHashSet --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index b9c6a15b..5b69f28e 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable; import java.util.HashMap; -import java.util.SortedSet; +import java.util.LinkedHashSet; public class VStyleSheet { private final HashMap, HashMap> widgetSpecificStyles = new HashMap<>(); @@ -71,7 +71,7 @@ public HashMap getClassStyles(String clazz) { return styleClasses.getOrDefault(clazz, new HashMap<>()); } - public HashMap mixClasses(SortedSet classes) { + public HashMap mixClasses(LinkedHashSet classes) { final HashMap values = new HashMap<>(); for (String clazz : classes) { From 4379cd32c52ab67ebe03f64e1a92a33864f1b42b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 01:02:10 +0200 Subject: [PATCH 071/661] new drag stuff --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 00484a58..94b91a73 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -49,11 +49,11 @@ public void init() { VLabel label = new VLabel("Hello world!", this).alsoAdd(); - label.onMouseDragLeft((oldX, oldY, newX, newY) -> setCursorShape(VCursorShape.VERTICAL_RESIZE)); + label.onMouseDragLeft((ctx) -> setCursorShape(VCursorShape.VERTICAL_RESIZE)); label.onLeftClickRelease(() -> setCursorShape(VCursorShape.DEFAULT)); - label.onMouseDragMiddle((oldX, oldY, newX, newY) -> setCursorShape(VCursorShape.ALL_RESIZE)); + label.onMouseDragMiddle((ctx) -> setCursorShape(VCursorShape.ALL_RESIZE)); label.onMiddleClickRelease(() -> setCursorShape(VCursorShape.DEFAULT)); - label.onMouseDragRight((oldX, oldY, newX, newY) -> setCursorShape(VCursorShape.HORIZONTAL_RESIZE)); + label.onMouseDragRight((ctx) -> setCursorShape(VCursorShape.HORIZONTAL_RESIZE)); label.onRightClickRelease(() -> setCursorShape(VCursorShape.DEFAULT)); label.onFilesDropped(System.out::println); From 60877f7ff1d045c6b85bee697f4d5cac84d98b82 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:05:05 +0200 Subject: [PATCH 072/661] fix just changed --- .../java/net/snackbag/mcvera/mixin/ParentElementMixin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index d686dc2e..bdc9689f 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -33,7 +33,8 @@ public interface ParentElementMixin { boolean finalJustChanged = justChanged; // weird java shit MCVeraData.asTopHierarchy(app -> { if (!app.isPointOverThis(mouseX, mouseY)) return; - if (!finalJustChanged) return; + if (finalJustChanged) return; + handleClickEvents(app.getTopWidgetAt(mouseX, mouseY), button); }); From 7e52b98935c4bfee246a854986f238159cbd9028 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:05:10 +0200 Subject: [PATCH 073/661] better syntax --- src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index bdc9689f..e4ba256a 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -38,7 +38,7 @@ public interface ParentElementMixin { handleClickEvents(app.getTopWidgetAt(mouseX, mouseY), button); }); - Vera.forAllVisibleApps((app) -> { + Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; VWidget hoveredWidget = app.getTopWidgetAt(mouseX, mouseY); From d53926b362d5130e73351ecb2238561d1d3c368d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:05:27 +0200 Subject: [PATCH 074/661] maybe also check if the mouse is actually hovering the app before doing stuff --- .../java/net/snackbag/mcvera/mixin/ParentElementMixin.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index e4ba256a..dc2c65e7 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -67,6 +67,8 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), button)); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; + if (!app.isPointOverThis(mouseX, mouseY)) return; + handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), button); }); } @@ -91,6 +93,8 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount)); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; + if (!app.isPointOverThis(mouseX, mouseY)) return; + handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount); }); } From d166f5832c7af8dfc9c667ffb8c69903fc939994 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:09:07 +0200 Subject: [PATCH 075/661] new mouse move handler --- .../net/snackbag/mcvera/mixin/MouseMixin.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index 4f486493..e02c1c55 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -2,7 +2,10 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.Mouse; +import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -23,11 +26,15 @@ public abstract class MouseMixin { double scaleFactor = client.getWindow().getScaleFactor(); - int scaledX = (int) (fx / scaleFactor); - int scaledY = (int) (fy / scaleFactor); + int mouseX = (int) (fx / scaleFactor); + int mouseY = (int) (fy / scaleFactor); - Vera.forHoveredWidget(scaledX, scaledY, (widget) -> { - widget.fireEvent("mouse-move", scaledX, scaledY); + VeraApp top = Vera.getTopHierarchyApp(); + Vera.forAllVisibleApps(app -> { + if (app.isRequiresHierarchy() && app != top) return; + + VWidget widget = app.getTopWidgetAt(mouseX, mouseY); + if (widget != null) widget.fireEvent("mouse-move", mouseX, mouseY); }); } From daa9069b12a05aae53daad8ba8ac69fc68db3fe4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:21:22 +0200 Subject: [PATCH 076/661] new handle file dropped method --- .../snackbag/mcvera/impl/MCVeraProvider.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index 4adb1c68..7df7d5f2 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -12,6 +12,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public class MCVeraProvider { public void handleAppInitialization(VeraApp app) { @@ -125,8 +126,29 @@ public void handleAppSetMouseRequired(VeraApp app, boolean mouseRequired) { } public void handleFilesDropped(List paths) { - Vera.forHoveredWidget(Vera.getMouseX(), Vera.getMouseY(), (widget) -> { - widget.fireEvent("files-dropped", paths); + VeraApp top = MCVeraData.getTopHierarchy(); + + int x = Vera.getMouseX(); + int y = Vera.getMouseY(); + + if (top != null && top.isPointOverThis(x, y)) { + VWidget widget = top.getTopWidgetAt(x, y); + if (widget != null) { + widget.fireEvent("files-dropped", paths); + return; + } + } + + AtomicBoolean didSomething = new AtomicBoolean(false); + Vera.forAllVisibleApps(app -> { + if (didSomething.get()) return; + if (!app.isPointOverThis(x, y)) return; + + VWidget widget = app.getTopWidgetAt(x, y); + if (widget != null) { + widget.fireEvent("files-dropped", paths); + didSomething.set(true); + } }); } } From b30091867d4e4371dafbd558bbff3c4db3719013 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:51:03 +0200 Subject: [PATCH 077/661] mark as no remap --- .../java/net/snackbag/mcvera/mixin/InGameHudMixin.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java index b0c8cdf1..754a63df 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java @@ -1,22 +1,17 @@ package net.snackbag.mcvera.mixin; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; -import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.LinkedHashSet; - @Mixin(InGameHud.class) public abstract class InGameHudMixin { - @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;enableBlend()V", shift = At.Shift.AFTER, ordinal = 0), method = "render") + @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;enableBlend()V", shift = At.Shift.AFTER, ordinal = 0, remap = false), method = "render") private void mcvera$renderBelowVignette(DrawContext context, float tickDelta, CallbackInfo ci) { Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_VIGNETTE); } From ca0d3efacd7eb0654f5a3bf99fcb19971d4e444b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:51:23 +0200 Subject: [PATCH 078/661] yeah this is cursed --- src/main/java/net/snackbag/vera/widget/VLabel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 7a70c72f..81d9449c 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -83,7 +83,8 @@ public VFont.FontModifier modifyFont(String key) { } public VColor.ColorModifier modifyFontColor(String key) { - return app.styleSheet.modifyKeyAsColor(this, key); + // TODO: More elegant solution + return new VColor.ColorModifier(((VFont) app.styleSheet.getKey(this, key)).getColor(), color -> app.styleSheet.modifyKeyAsFont(this, key).color(color)); } @Override From 13b06787bb49bed3690e4baabca4abedddf0d7fe Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 17 Jun 2025 16:53:05 +0200 Subject: [PATCH 079/661] turn path into identifier --- .../java/net/snackbag/vera/widget/VImage.java | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 59a2ce23..a7ce454f 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -5,30 +5,16 @@ import net.snackbag.vera.core.VeraApp; public class VImage extends VWidget { - private Identifier path; - public VImage(Identifier path, int width, int height, VeraApp app) { super(0, 0, width, height, app); - this.path = path; + setStyle("src", path); this.focusOnClick = false; } - public Identifier getPath() { - return path; - } - - public void setPath(Identifier path) { - this.path = path; - } - - public void setPath(String path) { - setPath(new Identifier(path)); - } - @Override public void render() { VeraApp app = getApp(); - Vera.renderer.drawImage(app, x, y, width, height, rotation, path); + Vera.renderer.drawImage(app, x, y, width, height, rotation, getStyle("path")); } } From dbde2566228184bd5594449bb5689ea9b83883d6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 11:46:53 +0200 Subject: [PATCH 080/661] whatever --- gradlew | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gradlew b/gradlew index f5feea6d..faf93008 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -206,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. From f473ae608cdc0effdc3aa53d040bb5329e485783 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:11:55 +0200 Subject: [PATCH 081/661] fix render crash --- src/main/java/net/snackbag/vera/widget/VImage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index a7ce454f..a33e3411 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -15,6 +15,6 @@ public VImage(Identifier path, int width, int height, VeraApp app) { @Override public void render() { VeraApp app = getApp(); - Vera.renderer.drawImage(app, x, y, width, height, rotation, getStyle("path")); + Vera.renderer.drawImage(app, x, y, width, height, rotation, getStyle("src")); } } From 52d7ccd261e929af29324cfa1dd1e45ab0ef2411 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:12:17 +0200 Subject: [PATCH 082/661] reserve src as IDENTIFIER --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 5b69f28e..407e7936 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -16,6 +16,7 @@ public class VStyleSheet { public VStyleSheet() { // Generic reserveType("background-color", StyleValueType.COLOR); + reserveType("src", StyleValueType.IDENTIFIER); // Font reserveType("color", StyleValueType.COLOR); From 4190c1ada0415609fd42cbce2fd7b0fc9ee5ef4b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:13:21 +0200 Subject: [PATCH 083/661] add IDENTIFIER --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 9db3e5a0..ecb88434 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -1,10 +1,14 @@ package net.snackbag.vera.style; +import net.minecraft.util.Identifier; +import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; +import org.jetbrains.annotations.Nullable; public enum StyleValueType { STRING(""), + IDENTIFIER(Identifier.of(MinecraftVera.MOD_ID, "empty")), INT(0), FLOAT(0.0F), From 8c271765f7657a116de7bdc133d653b4fa32f580 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:13:28 +0200 Subject: [PATCH 084/661] convert method --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index ecb88434..3225d97e 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -29,4 +29,9 @@ public static StyleValueType get(Object val) { else if (val instanceof VFont) return FONT; else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); } + + public static Object convert(Object value, StyleValueType to) { + if (to == IDENTIFIER && value instanceof String v) return new Identifier(v); + return value; + } } From 8a272094c02c7f338f6b79c4c160d79b001c6277 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:13:35 +0200 Subject: [PATCH 085/661] add bias to get --- .../java/net/snackbag/vera/style/StyleValueType.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 3225d97e..655c34a6 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -21,8 +21,13 @@ public enum StyleValueType { this.standard = standard; } - public static StyleValueType get(Object val) { - if (val instanceof String) return STRING; + public static StyleValueType get(Object val, @Nullable StyleValueType bias) { + if (val instanceof String s) { + if (bias == IDENTIFIER && s.matches("^[\\w-./]*:[\\w-./]*$")) return IDENTIFIER; + return STRING; + } + + else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof Integer) return INT; else if (val instanceof Float) return FLOAT; else if (val instanceof VColor) return COLOR; From c2be2fb7a7a70bd4d346b733b62c39e62b985347 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:14:24 +0200 Subject: [PATCH 086/661] mark double as castable to float --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 655c34a6..235cc100 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -29,7 +29,7 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof Integer) return INT; - else if (val instanceof Float) return FLOAT; + else if (val instanceof Float || val instanceof Double) return FLOAT; else if (val instanceof VColor) return COLOR; else if (val instanceof VFont) return FONT; else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); From 32d6ff9ac8c35fb1a3da0abcccdb0c0aeeda4d99 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:14:31 +0200 Subject: [PATCH 087/661] actually convert --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 407e7936..03932810 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -48,6 +48,8 @@ public void setKey(VWidget widget, String key, Object value) { throw new RuntimeException("Cannot set key %s, because it is reserved for type %s. Received: %s".formatted(key, res, valRes)); } else reserveType(key, valRes); + value = StyleValueType.convert(value, valRes); + if (!widgetSpecificStyles.containsKey(widget)) widgetSpecificStyles.put(widget, new HashMap<>()); widgetSpecificStyles.get(widget).put(key, value); } From 8da138794f72056c2d8b2c7435afeccd340201d6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:14:39 +0200 Subject: [PATCH 088/661] use bias on get --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 03932810..d6a54f67 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -41,7 +41,7 @@ public T getKey(VWidget widget, String key) { public void setKey(VWidget widget, String key, Object value) { StyleValueType res = getReservation(key); - StyleValueType valRes = StyleValueType.get(value); + StyleValueType valRes = StyleValueType.get(value, res); if (res != null) { if (valRes != res) From 8b78a29dd7895741a7f0de29b5d9bd99152bd901 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:25:58 +0200 Subject: [PATCH 089/661] cast double to float --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 235cc100..148c1c6a 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -37,6 +37,7 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { public static Object convert(Object value, StyleValueType to) { if (to == IDENTIFIER && value instanceof String v) return new Identifier(v); + else if (to == FLOAT && value instanceof Double v) return v.floatValue(); return value; } } From b26a120b53f7f3f14a79f42c08a3141a2176cbbf Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 13:32:26 +0200 Subject: [PATCH 090/661] reset cursor shape if not on widget --- src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index e02c1c55..da5dd9b4 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -4,6 +4,7 @@ import net.minecraft.client.Mouse; import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; @@ -35,6 +36,7 @@ public abstract class MouseMixin { VWidget widget = app.getTopWidgetAt(mouseX, mouseY); if (widget != null) widget.fireEvent("mouse-move", mouseX, mouseY); + else if (app.getCursorShape() != VCursorShape.DEFAULT) app.setCursorShape(VCursorShape.DEFAULT); }); } From 265004b787be6ad86db60f1fd9dba18430814b9d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 14:03:49 +0200 Subject: [PATCH 091/661] QOL constructor --- src/main/java/net/snackbag/vera/widget/VImage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index a7ce454f..6fadf20f 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -12,6 +12,10 @@ public VImage(Identifier path, int width, int height, VeraApp app) { this.focusOnClick = false; } + public VImage(String path, int width, int height, VeraApp app) { + this(new Identifier(path), width, height, app); + } + @Override public void render() { VeraApp app = getApp(); From 3e605c6edf242dbed9cfe77d81ced474848d49fa Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 14:05:02 +0200 Subject: [PATCH 092/661] replace identifier with shorter version --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 94b91a73..bbf3df0f 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -89,7 +89,7 @@ public void init() { rightLabel.onRightClick(() -> System.out.println(Vera.openFileSelector("test", Path.of("/Volumes/Media"), null))); VImage image = new VImage( - Identifier.of(Identifier.DEFAULT_NAMESPACE, "textures/block/dirt.png"), + "minecraft:textures/block/dirt.png", 32, 32, this).alsoAdd(); image.move(0, 30); image.onMiddleClick(this::hideCursor); From 9f3fa454650ba77f2ac0bc5b7c84618363c018f9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:36:32 +0200 Subject: [PATCH 093/661] rename IDLE -> DEFAULT --- src/main/java/net/snackbag/vera/style/StyleState.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/StyleState.java index 069ee866..ad08ea85 100644 --- a/src/main/java/net/snackbag/vera/style/StyleState.java +++ b/src/main/java/net/snackbag/vera/style/StyleState.java @@ -3,10 +3,10 @@ import org.jetbrains.annotations.Nullable; public enum StyleState { - IDLE("default"), - HOVERED("hover", IDLE), + DEFAULT("default"), + HOVERED("hover", DEFAULT), - CLICKED("any-click", IDLE), + CLICKED("any-click", DEFAULT), LEFT_CLICKED("left-click", CLICKED), MIDDLE_CLICKED("middle-click", CLICKED), RIGHT_CLICKED("right-click", CLICKED), From ee8e14c739ad4eed9f05d1c17de7ee23c839a1a3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:36:59 +0200 Subject: [PATCH 094/661] rename any-click -> clicked --- src/main/java/net/snackbag/vera/style/StyleState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/StyleState.java index ad08ea85..82f39d2b 100644 --- a/src/main/java/net/snackbag/vera/style/StyleState.java +++ b/src/main/java/net/snackbag/vera/style/StyleState.java @@ -6,7 +6,7 @@ public enum StyleState { DEFAULT("default"), HOVERED("hover", DEFAULT), - CLICKED("any-click", DEFAULT), + CLICKED("clicked", DEFAULT), LEFT_CLICKED("left-click", CLICKED), MIDDLE_CLICKED("middle-click", CLICKED), RIGHT_CLICKED("right-click", CLICKED), From bea4ff39bb462f5ce155a11b31db783de5565a6e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:45:14 +0200 Subject: [PATCH 095/661] allow for style states --- .../net/snackbag/vera/style/VStyleSheet.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index d6a54f67..aee91479 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -9,8 +9,8 @@ import java.util.LinkedHashSet; public class VStyleSheet { - private final HashMap, HashMap> widgetSpecificStyles = new HashMap<>(); - private final HashMap> styleClasses = new HashMap<>(); + private final HashMap, HashMap>> widgetSpecificStyles = new HashMap<>(); + private final HashMap>> styleClasses = new HashMap<>(); private final HashMap typeRegistry = new HashMap<>(); public VStyleSheet() { @@ -26,20 +26,30 @@ public VStyleSheet() { } public T getKey(VWidget widget, String key) { + return getKey(widget, key, StyleState.DEFAULT); + } + + public T getKey(VWidget widget, String key, StyleState state) { if (widgetSpecificStyles.containsKey(widget) && widgetSpecificStyles.get(widget).containsKey(key)) { - return (T) widgetSpecificStyles.get(widget).get(key); + if (!widgetSpecificStyles.get(widget).get(key).containsKey(state)) return getKey(widget, key, state.fallback); + return (T) widgetSpecificStyles.get(widget).get(key).get(state); } - HashMap mixed = mixClasses(widget.classes); + HashMap> mixed = mixClasses(widget.classes); if (mixed.containsKey(key)) { - return (T) mixed.get(key); + if (!mixed.get(key).containsKey(state)) return getKey(widget, key, state.fallback); + return (T) mixed.get(key).get(state); } return null; } - public void setKey(VWidget widget, String key, Object value) { + public void setKey(VWidget widget, String key, Object object) { + setKey(widget, key, object, StyleState.DEFAULT); + } + + public void setKey(VWidget widget, String key, Object value, StyleState state) { StyleValueType res = getReservation(key); StyleValueType valRes = StyleValueType.get(value, res); @@ -51,7 +61,8 @@ public void setKey(VWidget widget, String key, Object value) { value = StyleValueType.convert(value, valRes); if (!widgetSpecificStyles.containsKey(widget)) widgetSpecificStyles.put(widget, new HashMap<>()); - widgetSpecificStyles.get(widget).put(key, value); + if (!widgetSpecificStyles.get(widget).containsKey(key)) widgetSpecificStyles.get(widget).put(key, new HashMap<>()); + widgetSpecificStyles.get(widget).get(key).put(state, value); } public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { @@ -70,15 +81,15 @@ public void reserveType(String key, StyleValueType type) { return typeRegistry.getOrDefault(key, null); } - public HashMap getClassStyles(String clazz) { + public HashMap> getClassStyles(String clazz) { return styleClasses.getOrDefault(clazz, new HashMap<>()); } - public HashMap mixClasses(LinkedHashSet classes) { - final HashMap values = new HashMap<>(); + public HashMap> mixClasses(LinkedHashSet classes) { + final HashMap> values = new HashMap<>(); for (String clazz : classes) { - HashMap styles = getClassStyles(clazz); + HashMap> styles = getClassStyles(clazz); for (String key : styles.keySet()) values.put(key, styles.get(key)); } From 13bccec24f61bfad59781b4f71ff5caefea1a62c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:47:56 +0200 Subject: [PATCH 096/661] allow states in get/set style methods --- .../java/net/snackbag/vera/widget/VWidget.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 4364dbcc..113c4fbd 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -3,6 +3,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.*; +import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -150,15 +151,28 @@ public void setStyle(String key, Object value) { app.styleSheet.setKey(this, key, value); } + public void setStyle(String key, Object value, StyleState state) { + app.styleSheet.setKey(this, key, value, state); + } + public V getStyle(String key) { return app.styleSheet.getKey(this, key); } + public V getStyle(String key, StyleState state) { + return app.styleSheet.getKey(this, key, state); + } + public V getStyleOrDefault(String key, V dflt) { V style = getStyle(key); return style != null ? style : dflt; } + public V getStyleOrDefault(String key, V dflt, StyleState state) { + V style = getStyle(key); + return style != null ? style : dflt; + } + public void renderBorder() { // TODO: [Render Rework] Better border rendering From 73aea33a5661f943cc1adc3eff15406346dfdfe8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:49:25 +0200 Subject: [PATCH 097/661] add createStyleState method --- src/main/java/net/snackbag/vera/widget/VWidget.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 113c4fbd..b3add4c0 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -173,6 +173,11 @@ public V getStyleOrDefault(String key, V dflt, StyleState state) { return style != null ? style : dflt; } + public StyleState createStyleState() { + if (isHovered()) return StyleState.HOVERED; + else return StyleState.DEFAULT; + } + public void renderBorder() { // TODO: [Render Rework] Better border rendering From fe1a853dc1077c5c5bd9c92eee64ed695e3be13a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:52:31 +0200 Subject: [PATCH 098/661] use style state --- src/main/java/net/snackbag/vera/widget/VImage.java | 7 ++++++- src/main/java/net/snackbag/vera/widget/VLabel.java | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 7669a64e..dd5b7dd4 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -3,6 +3,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleState; public class VImage extends VWidget { public VImage(Identifier path, int width, int height, VeraApp app) { @@ -19,6 +20,10 @@ public VImage(String path, int width, int height, VeraApp app) { @Override public void render() { VeraApp app = getApp(); - Vera.renderer.drawImage(app, x, y, width, height, rotation, getStyle("src")); + + StyleState state = createStyleState(); + Identifier src = getStyle("src", state); + + Vera.renderer.drawImage(app, x, y, width, height, rotation, src); } } diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 81d9449c..d17894a4 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -3,6 +3,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.modifier.VPaddingWidget; +import net.snackbag.vera.style.StyleState; public class VLabel extends VWidget implements VPaddingWidget { private String text; @@ -68,7 +69,7 @@ public void setAlignment(VAlignmentFlag alignment) { } public void adjustSize() { - VFont font = getStyle("font"); + VFont font = getStyle("font", createStyleState()); this.width = Vera.provider.getTextWidth(text, font); this.height = Vera.provider.getTextHeight(text, font); @@ -90,7 +91,10 @@ public VColor.ColorModifier modifyFontColor(String key) { @Override public void render() { VeraApp app = getApp(); - VFont font = getStyle("font"); + + StyleState state = createStyleState(); + VFont font = getStyle("font", state); + VColor backgroundColor = getStyle("background-color", state); Vera.renderer.drawRect( app, @@ -99,7 +103,7 @@ public void render() { getHitboxWidth(), getHitboxHeight(), rotation, - getStyle("background-color") + backgroundColor ); switch (alignment) { From c37a20c60c8d1a8f1add5671459c5e749b022f1c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:52:41 +0200 Subject: [PATCH 099/661] actually check for state in state function --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index b3add4c0..7aa92802 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -169,7 +169,7 @@ public V getStyleOrDefault(String key, V dflt) { } public V getStyleOrDefault(String key, V dflt, StyleState state) { - V style = getStyle(key); + V style = getStyle(key, state); return style != null ? style : dflt; } From 16e70cfb19c2b7bd242dbf6f56a07ed7fd8f8338 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:53:33 +0200 Subject: [PATCH 100/661] set hover style --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index bbf3df0f..c8b523cd 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -7,6 +7,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.*; import java.nio.file.Path; @@ -94,6 +95,7 @@ public void init() { image.move(0, 30); image.onMiddleClick(this::hideCursor); image.onMiddleClickRelease(this::showCursor); + image.setStyle("src", "minecraft:textures/block/diamond.png", StyleState.HOVERED); VDropdown dropdown = new VDropdown(this).alsoAdd(); dropdown.addItem("coolio"); From 9b6e1f8bf1877aa25053a42e50b7a20799c13167 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 22:53:45 +0200 Subject: [PATCH 101/661] path -> src (in argument names) --- src/main/java/net/snackbag/vera/widget/VImage.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index dd5b7dd4..5a74fdca 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -6,15 +6,15 @@ import net.snackbag.vera.style.StyleState; public class VImage extends VWidget { - public VImage(Identifier path, int width, int height, VeraApp app) { + public VImage(Identifier src, int width, int height, VeraApp app) { super(0, 0, width, height, app); - setStyle("src", path); + setStyle("src", src); this.focusOnClick = false; } - public VImage(String path, int width, int height, VeraApp app) { - this(new Identifier(path), width, height, app); + public VImage(String src, int width, int height, VeraApp app) { + this(new Identifier(src), width, height, app); } @Override From 4e9a000d54705d273ff519140117e6860e0da790 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:04:29 +0200 Subject: [PATCH 102/661] im dumb --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index c8b523cd..79716f1e 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -95,7 +95,7 @@ public void init() { image.move(0, 30); image.onMiddleClick(this::hideCursor); image.onMiddleClickRelease(this::showCursor); - image.setStyle("src", "minecraft:textures/block/diamond.png", StyleState.HOVERED); + image.setStyle("src", "minecraft:textures/block/diamond_block.png", StyleState.HOVERED); VDropdown dropdown = new VDropdown(this).alsoAdd(); dropdown.addItem("coolio"); From 2e120d56855f216f791e60f8efe8ad4b6e1870aa Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:18:40 +0200 Subject: [PATCH 103/661] render overlay method for every widget now --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 1 + src/main/java/net/snackbag/vera/widget/VWidget.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 3c72d454..9db8ca21 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -77,6 +77,7 @@ public void renderApp(VeraApp app) { if (widget.visibilityConditionsPassed()) { widget.render(); widget.renderBorder(); + widget.renderOverlay(); } } app.renderAfterWidgets(); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 7aa92802..b94ef6d0 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -206,6 +206,12 @@ public void renderBorder() { } } + public void renderOverlay() { + StyleState state = createStyleState(); + + Vera.renderer.drawRect(app, x, y, width, height, 0, getStyle("overlay", state)); + } + public void setSize(int width, int height) { setWidth(width); setHeight(height); From c72327ae46692aa1342c1b2e3a267a7d1c36d831 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:18:51 +0200 Subject: [PATCH 104/661] update checkbox to new system --- .../net/snackbag/vera/widget/VCheckBox.java | 111 +++--------------- 1 file changed, 17 insertions(+), 94 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index bf5bc6f0..299bcfc7 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -7,67 +7,41 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VCheckedStateChange; -import org.jetbrains.annotations.Nullable; +import net.snackbag.vera.style.StyleState; public class VCheckBox extends VWidget { private boolean checked; - private Identifier checkedTexture; - private Identifier defaultTexture; - private VColor hoverOverlayColor; - - private @Nullable Identifier checkedHoverTexture; - private @Nullable Identifier defaultHoverTexture; - public VCheckBox(VeraApp app) { - super(0, 0, 15, 15, app); - - this.checked = false; - - this.checkedTexture = new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/checked.png"); - this.defaultTexture = new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/default.png"); - this.hoverOverlayColor = VColor.transparent(); - - setHoverCursor(VCursorShape.POINTING_HAND); + this( + app, + new Identifier(MinecraftVera.MOD_ID, "widget/checkmark/default.png"), + new Identifier(MinecraftVera.MOD_ID, "widget/checkmark/checked.png") + ); } public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTexture) { - this(app); - - this.defaultTexture = defaultTexture; - this.checkedTexture = checkedTexture; + this(app, defaultTexture, checkedTexture, 15, 15); } public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTexture, int width, int height) { - this(app, defaultTexture, checkedTexture); - - setSize(width, height); - } + super(0, 0, width, height, app); - public VCheckBox( - VeraApp app, - Identifier defaultTexture, @Nullable Identifier defaultHoverTexture, - Identifier checkedTexture, @Nullable Identifier checkedHoverTexture) { - this(app, defaultTexture, checkedTexture); - - this.defaultHoverTexture = defaultHoverTexture; - this.checkedHoverTexture = checkedHoverTexture; - } + this.checked = false; - public VCheckBox( - VeraApp app, - Identifier defaultTexture, @Nullable Identifier defaultHoverTexture, - Identifier checkedTexture, @Nullable Identifier checkedHoverTexture, - int width, int height) { - this(app, defaultTexture, defaultHoverTexture, checkedTexture, checkedHoverTexture); + setStyle("src", defaultTexture); + setStyle("src-checked", checkedTexture); + setStyle("overlay", VColor.transparent()); - setSize(width, height); + setHoverCursor(VCursorShape.POINTING_HAND); } @Override public void render() { - Vera.renderer.drawImage(app, x, y, width, height, 0, getCurrentTexture()); - if (isHovered()) Vera.renderer.drawRect(app, x, y, width, height, 0, hoverOverlayColor); + StyleState state = createStyleState(); + Identifier texture = checked ? getStyle("src-checked", state) : getStyle("src", state); + + Vera.renderer.drawImage(app, x, y, width, height, 0, texture); } @Override @@ -76,57 +50,6 @@ public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); } - public Identifier getCurrentTexture() { - if (isHovered() && isChecked()) return getCheckedHoverTexture(); - else if (isHovered()) return getDefaultHoverTexture(); - else if (isChecked()) return getCheckedTexture(); - else return getDefaultTexture(); - } - - public Identifier getCheckedTexture() { - return checkedTexture; - } - - public void setCheckedTexture(Identifier checkedTexture) { - this.checkedTexture = checkedTexture; - } - - public void setDefaultTexture(Identifier defaultTexture) { - this.defaultTexture = defaultTexture; - } - - public Identifier getDefaultTexture() { - return defaultTexture; - } - - public Identifier getCheckedHoverTexture() { - return checkedHoverTexture != null ? checkedHoverTexture : checkedTexture; - } - - public void setCheckedHoverTexture(@Nullable Identifier checkedHoverTexture) { - this.checkedHoverTexture = checkedHoverTexture; - } - - public Identifier getDefaultHoverTexture() { - return defaultHoverTexture != null ? defaultHoverTexture : defaultTexture; - } - - public void setDefaultHoverTexture(@Nullable Identifier defaultHoverTexture) { - this.defaultHoverTexture = defaultHoverTexture; - } - - public VColor getHoverOverlayColor() { - return hoverOverlayColor; - } - - public void setHoverOverlayColor(VColor hoverOverlayColor) { - this.hoverOverlayColor = hoverOverlayColor; - } - - public VColor.ColorModifier modifyHoverOverlayColor() { - return new VColor.ColorModifier(hoverOverlayColor, this::setHoverOverlayColor); - } - public boolean isChecked() { return checked; } From c129a440bfb24401220398e6e2ac48ef5c27ae8e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:19:53 +0200 Subject: [PATCH 105/661] click fallback is now hover --- src/main/java/net/snackbag/vera/style/StyleState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/StyleState.java index 82f39d2b..82efbe00 100644 --- a/src/main/java/net/snackbag/vera/style/StyleState.java +++ b/src/main/java/net/snackbag/vera/style/StyleState.java @@ -6,7 +6,7 @@ public enum StyleState { DEFAULT("default"), HOVERED("hover", DEFAULT), - CLICKED("clicked", DEFAULT), + CLICKED("clicked", HOVERED), LEFT_CLICKED("left-click", CLICKED), MIDDLE_CLICKED("middle-click", CLICKED), RIGHT_CLICKED("right-click", CLICKED), From ae39488996f6e7d5bb858de2e0485a4737335ba3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:20:04 +0200 Subject: [PATCH 106/661] overlay --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 79716f1e..c92d84a2 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -112,7 +112,7 @@ public void init() { VCheckBox checkbox = new VCheckBox(this).alsoAdd(); checkbox.move(20, 140); - checkbox.setHoverOverlayColor(VColor.white().withOpacity(0.4f)); + checkbox.setStyle("overlay", VColor.white().withOpacity(0.4f), StyleState.HOVERED); checkbox.onCheckStateChange((state) -> { if (!state) removeWidget(checkbox); From 2cb3b461b9888f0aa9425260af573dd5add476e3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:21:02 +0200 Subject: [PATCH 107/661] reserve src-checked and overlay --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index aee91479..714b559d 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -15,9 +15,13 @@ public class VStyleSheet { public VStyleSheet() { // Generic + reserveType("overlay", StyleValueType.COLOR); reserveType("background-color", StyleValueType.COLOR); reserveType("src", StyleValueType.IDENTIFIER); + // Checkbox + reserveType("src-checked", StyleValueType.IDENTIFIER); + // Font reserveType("color", StyleValueType.COLOR); reserveType("font", StyleValueType.FONT); From a2fb8532d40d7e93bd04e64bede309275dbcdf56 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:23:30 +0200 Subject: [PATCH 108/661] set overlay color to prevent crash --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index b94ef6d0..4df0a17c 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -47,6 +47,8 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { this.borderSize = new V4Int(0); addVisibilityCondition(this::isVisible); + + setStyle("overlay", VColor.transparent()); } public abstract void render(); From f7215295f68fc947af3188b306675667466f14f4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:26:43 +0200 Subject: [PATCH 109/661] add cursor type --- .../java/net/snackbag/vera/style/StyleValueType.java | 12 ++++++++++-- .../java/net/snackbag/vera/style/VStyleSheet.java | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 148c1c6a..b4639144 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -3,7 +3,9 @@ import net.minecraft.util.Identifier; import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; +import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; public enum StyleValueType { @@ -13,7 +15,8 @@ public enum StyleValueType { FLOAT(0.0F), COLOR(VColor.black()), - FONT(VFont.create()); + FONT(VFont.create()), + CURSOR(VCursorShape.DEFAULT); public final Object standard; @@ -24,6 +27,7 @@ public enum StyleValueType { public static StyleValueType get(Object val, @Nullable StyleValueType bias) { if (val instanceof String s) { if (bias == IDENTIFIER && s.matches("^[\\w-./]*:[\\w-./]*$")) return IDENTIFIER; + else if (bias == CURSOR && EnumUtils.getEnumIgnoreCase(VCursorShape.class, s) != null) return CURSOR; return STRING; } @@ -36,7 +40,11 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { } public static Object convert(Object value, StyleValueType to) { - if (to == IDENTIFIER && value instanceof String v) return new Identifier(v); + if (value instanceof String v) { + if (to == IDENTIFIER) return new Identifier(v); + else if (to == CURSOR) return EnumUtils.getEnumIgnoreCase(VCursorShape.class, v); + } + else if (to == FLOAT && value instanceof Double v) return v.floatValue(); return value; } diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 714b559d..7bf96e38 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -18,6 +18,7 @@ public VStyleSheet() { reserveType("overlay", StyleValueType.COLOR); reserveType("background-color", StyleValueType.COLOR); reserveType("src", StyleValueType.IDENTIFIER); + reserveType("cursor", StyleValueType.CURSOR); // Checkbox reserveType("src-checked", StyleValueType.IDENTIFIER); From cfa7b971a9941a4f90dd96fac7e5c6e9dd230c1e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:27:03 +0200 Subject: [PATCH 110/661] fix asset typo --- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 299bcfc7..38cddd32 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -15,8 +15,8 @@ public class VCheckBox extends VWidget { public VCheckBox(VeraApp app) { this( app, - new Identifier(MinecraftVera.MOD_ID, "widget/checkmark/default.png"), - new Identifier(MinecraftVera.MOD_ID, "widget/checkmark/checked.png") + new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/default.png"), + new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/checked.png") ); } From c390818b2cea1b3aeb2f675c4fc8ecb8a2b58363 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:30:22 +0200 Subject: [PATCH 111/661] set cursor new way --- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 2 +- src/main/java/net/snackbag/vera/widget/VDropdown.java | 4 +++- src/main/java/net/snackbag/vera/widget/VLineInput.java | 3 ++- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 38cddd32..2e81db5c 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -33,7 +33,7 @@ public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTextu setStyle("src-checked", checkedTexture); setStyle("overlay", VColor.transparent()); - setHoverCursor(VCursorShape.POINTING_HAND); + setStyle("cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 8ce4292f..70104d55 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -5,6 +5,7 @@ import net.snackbag.vera.core.*; import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VPaddingWidget; +import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -31,7 +32,8 @@ public VDropdown(VeraApp app) { backgroundColor = VColor.white(); itemHoverColor = VColor.white().sub(30); padding = new V4Int(5, 10); - setHoverCursor(VCursorShape.POINTING_HAND); + + setStyle("cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index df2ff080..edfcce3d 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -6,6 +6,7 @@ import net.snackbag.vera.core.*; import net.snackbag.vera.event.VCharLimitedEvent; import net.snackbag.vera.modifier.VPaddingWidget; +import net.snackbag.vera.style.StyleState; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -41,7 +42,7 @@ public VLineInput(VeraApp app) { this.backgroundColor = VColor.transparent(); this.padding = new V4Int(4); - setHoverCursor(VCursorShape.TEXT); + setStyle("cursor", VCursorShape.TEXT, StyleState.HOVERED); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 9f68fb0b..a5d2db71 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -6,6 +6,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -29,7 +30,7 @@ public VTabWidget(VeraApp app, String... tabs) { selectedBackgroundColor = VColor.white(); defaultBackgroundColor = VColor.white().sub(40); - setHoverCursor(VCursorShape.POINTING_HAND); + setStyle("cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } @Override From 78fbd91b186d893349b6bee00240690cac56ad4c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:30:31 +0200 Subject: [PATCH 112/661] move away from old cursor --- src/main/java/net/snackbag/vera/widget/VWidget.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 4df0a17c..7d663ca5 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -21,7 +21,6 @@ public abstract class VWidget> { protected V4Int borderSize; protected VeraApp app; - protected VCursorShape hoverCursor = VCursorShape.DEFAULT; protected boolean focusOnClick = true; private boolean hovered = false; private boolean visible = true; @@ -400,6 +399,8 @@ public void clearEventsFor(String event) { } public void handleBuiltinEvent(String event, Object... args) { + StyleState state = createStyleState(); + switch (event) { case "left-click" -> { if (shouldFocusOnClick()) { @@ -415,7 +416,7 @@ public void handleBuiltinEvent(String event, Object... args) { case "right-click-release" -> clearRightClickDown(); case "middle-click-release" -> clearMiddleClickDown(); - case "hover" -> app.setCursorShape(hoverCursor); + case "hover" -> app.setCursorShape(getStyle("cursor", state)); case "hover-leave" -> { clearLeftClickDown(); clearRightClickDown(); @@ -436,14 +437,6 @@ private void clearMiddleClickDown() { middleClickDown = false; } - public VCursorShape getHoverCursor() { - return hoverCursor; - } - - public void setHoverCursor(@Nullable VCursorShape hoverCursor) { - this.hoverCursor = hoverCursor == null ? VCursorShape.DEFAULT : hoverCursor; - } - public boolean isFocused() { return app.isFocusedWidget(this); } From 1cbd722c0ee4dda93e858a12c1c98ec6a196d2c0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:31:19 +0200 Subject: [PATCH 113/661] new cursor system --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index c92d84a2..1f48615b 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -78,7 +78,7 @@ public void init() { centerLabel.move(220, 10); centerLabel.setBorder(VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); centerLabel.setBorderSize(5, 10, 8, 16); - centerLabel.setHoverCursor(VCursorShape.ALL_RESIZE); + centerLabel.setStyle("cursor", VCursorShape.ALL_RESIZE); VLabel rightLabel = new VLabel("RIGHT", this).alsoAdd(); rightLabel.setAlignment(VAlignmentFlag.RIGHT); From 120ee9d461d9ba75522103a0cac91f9eb520285a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:32:00 +0200 Subject: [PATCH 114/661] fix crash --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index b4639144..0cf926b1 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -32,6 +32,7 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { } else if (val instanceof Identifier) return IDENTIFIER; + else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof Integer) return INT; else if (val instanceof Float || val instanceof Double) return FLOAT; else if (val instanceof VColor) return COLOR; From 48c0f4c8101b1cab95cac1226fee34df0a01bc5a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:34:06 +0200 Subject: [PATCH 115/661] fix NPE --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 7bf96e38..d8115ab3 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -34,7 +34,9 @@ public T getKey(VWidget widget, String key) { return getKey(widget, key, StyleState.DEFAULT); } - public T getKey(VWidget widget, String key, StyleState state) { + public T getKey(VWidget widget, String key, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + if (widgetSpecificStyles.containsKey(widget) && widgetSpecificStyles.get(widget).containsKey(key)) { if (!widgetSpecificStyles.get(widget).get(key).containsKey(state)) return getKey(widget, key, state.fallback); return (T) widgetSpecificStyles.get(widget).get(key).get(state); @@ -54,7 +56,9 @@ public void setKey(VWidget widget, String key, Object object) { setKey(widget, key, object, StyleState.DEFAULT); } - public void setKey(VWidget widget, String key, Object value, StyleState state) { + public void setKey(VWidget widget, String key, Object value, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + StyleValueType res = getReservation(key); StyleValueType valRes = StyleValueType.get(value, res); From 32d03ef937a585525e0d0f842fad3b890408e259 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:34:14 +0200 Subject: [PATCH 116/661] set style cursor --- src/main/java/net/snackbag/vera/widget/VWidget.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 7d663ca5..db37d28b 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -48,6 +48,7 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { addVisibilityCondition(this::isVisible); setStyle("overlay", VColor.transparent()); + setStyle("cursor", VCursorShape.DEFAULT); } public abstract void render(); From 3d41b4186f552cab3d7d25055a645e2d790c581b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:39:51 +0200 Subject: [PATCH 117/661] this is a method?? --- src/main/java/net/snackbag/vera/widget/VWidget.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index db37d28b..c793a3bb 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -28,6 +28,7 @@ public abstract class VWidget> { private boolean leftClickDown = false; private boolean middleClickDown = false; private boolean rightClickDown = false; + private StyleState prevStyleState = StyleState.DEFAULT; private final HashMap> eventExecutors; private final List> visibilityConditions; @@ -260,7 +261,11 @@ public void rotate(double rotation) { this.rotation = rotation; } - public void update() {} + public void update() { + StyleState state = createStyleState(); + + app.setCursorShape(getStyle("cursor", state)); + } public boolean isHovered() { return hovered; @@ -401,6 +406,7 @@ public void clearEventsFor(String event) { public void handleBuiltinEvent(String event, Object... args) { StyleState state = createStyleState(); + if (state != prevStyleState) update(); switch (event) { case "left-click" -> { @@ -424,6 +430,8 @@ public void handleBuiltinEvent(String event, Object... args) { clearMiddleClickDown(); } } + + prevStyleState = state; } private void clearLeftClickDown() { From 9cb9a35a0796b6f811241832f727743ae6d4c973 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:45:51 +0200 Subject: [PATCH 118/661] mark select-color as reserved for color --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index d8115ab3..31f81be1 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -23,6 +23,9 @@ public VStyleSheet() { // Checkbox reserveType("src-checked", StyleValueType.IDENTIFIER); + // Line Input + reserveType("select-color", StyleValueType.COLOR); + // Font reserveType("color", StyleValueType.COLOR); reserveType("font", StyleValueType.FONT); From fc450cc3eae07b6f2ff24a5cb1314e215a5fd2a0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:46:23 +0200 Subject: [PATCH 119/661] textSelectionColor and backgroundColor are now normal styles --- .../net/snackbag/vera/widget/VLineInput.java | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index edfcce3d..e7f373aa 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -20,10 +20,8 @@ public class VLineInput extends VWidget implements VPaddingWidget { private @Nullable VColor cursorColor; private int cursorPos; private TextSelection textSelection; - private VColor textSelectionColor; private int maxChars; - private VColor backgroundColor; private V4Int padding; public VLineInput(VeraApp app) { @@ -36,10 +34,10 @@ public VLineInput(VeraApp app) { this.cursorColor = null; this.cursorPos = 0; this.textSelection = new TextSelection(); - this.textSelectionColor = VColor.of(0, 120, 215, 0.2f); + setStyle("select-color", VColor.of(0, 120, 215, 0.2f)); this.maxChars = -1; - this.backgroundColor = VColor.transparent(); + setStyle("background-color", VColor.transparent()); this.padding = new V4Int(4); setStyle("cursor", VCursorShape.TEXT, StyleState.HOVERED); @@ -47,6 +45,11 @@ public VLineInput(VeraApp app) { @Override public void render() { + StyleState state = createStyleState(); + + VColor backgroundColor = getStyle("background-color", state); + VColor textSelectionColor = getStyle("select-color", state); + Vera.renderer.drawRect( app, getHitboxX() + app.getX(), @@ -136,18 +139,6 @@ public VColor.ColorModifier modifyPlaceholderFontColor() { return new VColor.ColorModifier(font.getColor(), (color) -> setFont(font.withColor(color))); } - public VColor getBackgroundColor() { - return backgroundColor; - } - - public void setBackgroundColor(VColor backgroundColor) { - this.backgroundColor = backgroundColor; - } - - public VColor.ColorModifier modifyBackgroundColor() { - return new VColor.ColorModifier(backgroundColor, this::setBackgroundColor); - } - public String getText() { return text; } @@ -178,18 +169,6 @@ public void setTextSelection(int start, int end) { textSelection.setEndPos(end); } - public VColor getTextSelectionColor() { - return textSelectionColor; - } - - public void setTextSelectionColor(VColor textSelectionColor) { - this.textSelectionColor = textSelectionColor; - } - - public VColor.ColorModifier modifyTextSelectionColor() { - return new VColor.ColorModifier(textSelectionColor, this::setTextSelectionColor); - } - public int getMaxChars() { return maxChars; } From f1e511f19466bea6cbcc0b6753af0c89bbaeac65 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:47:22 +0200 Subject: [PATCH 120/661] color is now a style --- .../java/net/snackbag/vera/widget/VRect.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 59fbf060..7a793b5c 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -3,31 +3,20 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleState; public class VRect extends VWidget { - protected VColor color; - public VRect(VColor color, VeraApp app) { super(0, 0, 20, 20, app); - this.color = color; + setStyle("color", color); this.focusOnClick = false; } - public VColor getColor() { - return color; - } - - public void setColor(VColor color) { - this.color = color; - } - - public VColor.ColorModifier modifyColor() { - return new VColor.ColorModifier(color, this::setColor); - } - @Override public void render() { - Vera.renderer.drawRect(app, x, y, width, height, rotation, color); + StyleState state = createStyleState(); + + Vera.renderer.drawRect(app, x, y, width, height, rotation, getStyle("color", state)); } } From c54b0ec02d7d5467564b7c2c9504f1977e650c6f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 18 Jun 2025 23:48:48 +0200 Subject: [PATCH 121/661] background color is now a style --- .../net/snackbag/vera/widget/VDropdown.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 70104d55..c7bb187e 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -15,7 +15,6 @@ public class VDropdown extends VWidget implements VPaddingWidget { private final List items; private VFont font; private VFont hoverFont; - private VColor backgroundColor; private VColor itemHoverColor; private V4Int padding; @@ -29,7 +28,7 @@ public VDropdown(VeraApp app) { items = new ArrayList<>(); font = VFont.create(); hoverFont = VFont.create(); - backgroundColor = VColor.white(); + setStyle("background-color", VColor.white()); itemHoverColor = VColor.white().sub(30); padding = new V4Int(5, 10); @@ -38,6 +37,9 @@ public VDropdown(VeraApp app) { @Override public void render() { + StyleState state = createStyleState(); + VColor backgroundColor = getStyle("background-color", state); + Vera.renderer.drawRect( app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), 0, backgroundColor @@ -279,18 +281,6 @@ public VColor.ColorModifier modifyFontColor() { return new VColor.ColorModifier(font.getColor(), (color) -> setFont(font.withColor(color))); } - public VColor getBackgroundColor() { - return backgroundColor; - } - - public void setBackgroundColor(VColor backgroundColor) { - this.backgroundColor = backgroundColor; - } - - public VColor.ColorModifier modifyBackgroundColor() { - return new VColor.ColorModifier(backgroundColor, this::setBackgroundColor); - } - public void addItem(String name) { items.add(new Item(name, null, null, null, null, null)); } From 386c66bebeb1103b11dd7b1257b1ba19909a1c6e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 08:53:38 +0200 Subject: [PATCH 122/661] fix input style --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 1f48615b..10f40eda 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -45,7 +45,7 @@ public void init() { input.onMouseMove((x, y) -> System.out.println("x=" + x + ", y=" + y)); input.move(50); - input.setBackgroundColor(VColor.white()); + input.setStyle("background-color", VColor.white()); setFocusedWidget(input); VLabel label = new VLabel("Hello world!", this).alsoAdd(); From f02a0fca2ac158e7d15ac99f293bfd068a86f6dc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 09:02:44 +0200 Subject: [PATCH 123/661] add test commands --- .../net/snackbag/mcvera/InternalCommands.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/InternalCommands.java diff --git a/src/main/java/net/snackbag/mcvera/InternalCommands.java b/src/main/java/net/snackbag/mcvera/InternalCommands.java new file mode 100644 index 00000000..cff43a31 --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/InternalCommands.java @@ -0,0 +1,30 @@ +package net.snackbag.mcvera; + +import com.mojang.brigadier.CommandDispatcher; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.command.CommandRegistryAccess; +import net.snackbag.mcvera.test.StyleTestApplication; +import net.snackbag.mcvera.test.TestApplication; + +public class InternalCommands { + public static void register( + CommandDispatcher dispatcher, + CommandRegistryAccess ra + ) { + if (!FabricLoader.getInstance().isDevelopmentEnvironment()) return; + + dispatcher.register( + ClientCommandManager.literal("vera") + .then(ClientCommandManager.literal("test1").executes((ctx) -> { + TestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("test2").executes((ctx) -> { + StyleTestApplication.INSTANCE.show(); + return 1; + })) + ); + } +} From 57ca6b234fbd4241b89b9e600a73abfed5ba5452 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 09:02:53 +0200 Subject: [PATCH 124/661] clean up imports --- src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java index 996cdf5d..d3fad0d8 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVeraClient.java @@ -4,15 +4,12 @@ import com.google.gson.JsonObject; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.InputUtil; import net.minecraft.resource.Resource; import net.minecraft.util.Identifier; -import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.mcvera.test.TestHandler; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; import java.io.IOException; import java.io.InputStreamReader; From fc091ee22939a8ec3f8d638afa84983c11789e60 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 09:03:09 +0200 Subject: [PATCH 125/661] register commands instead of keybinds --- .../net/snackbag/mcvera/test/TestHandler.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestHandler.java b/src/main/java/net/snackbag/mcvera/test/TestHandler.java index 1c4cc8dc..f95f58b8 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestHandler.java +++ b/src/main/java/net/snackbag/mcvera/test/TestHandler.java @@ -1,10 +1,8 @@ package net.snackbag.mcvera.test; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.util.InputUtil; -import org.lwjgl.glfw.GLFW; +import net.snackbag.mcvera.InternalCommands; public class TestHandler { public static boolean shouldTest() { @@ -14,14 +12,6 @@ public static boolean shouldTest() { public static void impl(boolean force) { if (!(force || shouldTest())) return; - ClientTickEvents.END_CLIENT_TICK.register((client) -> { - if (InputUtil.isKeyPressed(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_APOSTROPHE)) { - if (InputUtil.isKeyPressed(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_SHIFT)) { - StyleTestApplication.INSTANCE.show(); - } else { - TestApplication.INSTANCE.setVisibility(true); - } - } - }); + ClientCommandRegistrationCallback.EVENT.register(InternalCommands::register); } } From 622fc866fee85b1c252ce8f89f21e27951d7a22b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 09:07:22 +0200 Subject: [PATCH 126/661] add escape exit shortcut --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index b517f63f..5d61512e 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -3,6 +3,7 @@ import net.minecraft.util.Identifier; import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.widget.VLabel; public class StyleTestApplication extends VeraApp { @@ -10,6 +11,8 @@ public class StyleTestApplication extends VeraApp { @Override public void init() { + new VShortcut(this, "escape", this::hide).alsoAdd(); + //loadStyleSheet(new Identifier(MinecraftVera.MOD_ID, "demo/test.vss")); new VLabel("helo", this).alsoAdd(); From b90956c9805d5c3c8a659a641b06852848687c0e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 09:09:43 +0200 Subject: [PATCH 127/661] make some variables public --- src/main/java/net/snackbag/vera/event/VShortcut.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/event/VShortcut.java b/src/main/java/net/snackbag/vera/event/VShortcut.java index cc7cc76d..1430a587 100644 --- a/src/main/java/net/snackbag/vera/event/VShortcut.java +++ b/src/main/java/net/snackbag/vera/event/VShortcut.java @@ -5,9 +5,9 @@ import org.apache.commons.lang3.SystemUtils; public class VShortcut { - private final VeraApp app; + public final VeraApp app; private final String combination; - private final boolean transformOSX; + public final boolean transformOSX; private Runnable event; public VShortcut(VeraApp app, String combination, Runnable event) { @@ -21,10 +21,6 @@ public VShortcut(VeraApp app, String combination, Runnable event, boolean transf this.transformOSX = transformOSX; } - public VeraApp getApp() { - return app; - } - public String getCombination() { return applyTransformations(transformOSX, combination); } @@ -45,10 +41,6 @@ public void setEvent(Runnable event) { this.event = event; } - public boolean shouldTransformOSX() { - return transformOSX; - } - public void run() { Vera.provider.handleRunShortcut(this); } From 2b87ba15c4732b056ed851dbb129279ea122f396 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 09:54:57 +0200 Subject: [PATCH 128/661] make focusOnClick public --- src/main/java/net/snackbag/vera/widget/VWidget.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index c793a3bb..ca3cb588 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -21,7 +21,7 @@ public abstract class VWidget> { protected V4Int borderSize; protected VeraApp app; - protected boolean focusOnClick = true; + public boolean focusOnClick = true; private boolean hovered = false; private boolean visible = true; @@ -410,7 +410,7 @@ public void handleBuiltinEvent(String event, Object... args) { switch (event) { case "left-click" -> { - if (shouldFocusOnClick()) { + if (focusOnClick) { setFocused(true); } leftClickDown = true; @@ -455,14 +455,6 @@ public void setFocused(boolean focused) { else app.setFocusedWidget(null); } - public boolean shouldFocusOnClick() { - return focusOnClick; - } - - public void setFocusOnClick(boolean focus) { - focusOnClick = focus; - } - public void keyPressed(int keyCode, int scanCode, int modifiers) {} public void charTyped(char chr, int modifiers) {} From a7398bdd401dff7d52a86078e815e689cc035e5c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 10:36:48 +0200 Subject: [PATCH 129/661] add V4Int and V4Color support --- .../snackbag/vera/style/StyleValueType.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 0cf926b1..99eb904a 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -2,9 +2,7 @@ import net.minecraft.util.Identifier; import net.snackbag.mcvera.MinecraftVera; -import net.snackbag.vera.core.VColor; -import net.snackbag.vera.core.VCursorShape; -import net.snackbag.vera.core.VFont; +import net.snackbag.vera.core.*; import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; @@ -16,7 +14,10 @@ public enum StyleValueType { COLOR(VColor.black()), FONT(VFont.create()), - CURSOR(VCursorShape.DEFAULT); + CURSOR(VCursorShape.DEFAULT), + + V4INT(new V4Int(0)), + V4COLOR(new V4Color(VColor.black())); public final Object standard; @@ -31,6 +32,8 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { return STRING; } + else if (val instanceof V4Color || val instanceof VColor[]) return V4COLOR; + else if (val instanceof V4Int || val instanceof int[]) return V4INT; else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof Integer) return INT; @@ -46,6 +49,24 @@ public static Object convert(Object value, StyleValueType to) { else if (to == CURSOR) return EnumUtils.getEnumIgnoreCase(VCursorShape.class, v); } + else if (value instanceof int[] v) { + return switch (v.length) { + case 1 -> new V4Int(v[0]); + case 2 -> new V4Int(v[0], v[1]); + case 4 -> new V4Int(v[0], v[1], v[2], v[3]); + default -> throw new RuntimeException("invalid V4Int format. Length must be 1, 2 or 4. Provided: %d".formatted(v.length)); + }; + } + + else if (value instanceof VColor[] v) { + return switch (v.length) { + case 1 -> new V4Color(v[0]); + case 2 -> new V4Color(v[0], v[1]); + case 4 -> new V4Color(v[0], v[1], v[2], v[3]); + default -> throw new RuntimeException("invalid V4Color format. Length must be 1, 2 or 4. Provided: %d".formatted(v.length)); + }; + } + else if (to == FLOAT && value instanceof Double v) return v.floatValue(); return value; } From e0df16458999802cb8bdac50578a8abfdf90ea08 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 19 Jun 2025 10:52:58 +0200 Subject: [PATCH 130/661] cursor color style --- .../java/net/snackbag/vera/style/VStyleSheet.java | 1 + .../java/net/snackbag/vera/widget/VLineInput.java | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 31f81be1..c4af4010 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -25,6 +25,7 @@ public VStyleSheet() { // Line Input reserveType("select-color", StyleValueType.COLOR); + reserveType("color-cursor", StyleValueType.COLOR); // Font reserveType("color", StyleValueType.COLOR); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index e7f373aa..23d7bb62 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -17,7 +17,6 @@ public class VLineInput extends VWidget implements VPaddingWidget { private VFont font; private VFont placeholderFont; - private @Nullable VColor cursorColor; private int cursorPos; private TextSelection textSelection; private int maxChars; @@ -31,7 +30,6 @@ public VLineInput(VeraApp app) { this.placeholderText = ""; this.font = VFont.create(); this.placeholderFont = VFont.create().withColor(VColor.black().withOpacity(0.5f)); - this.cursorColor = null; this.cursorPos = 0; this.textSelection = new TextSelection(); setStyle("select-color", VColor.of(0, 120, 215, 0.2f)); @@ -413,16 +411,10 @@ public void setCursorPos(int cursorPos) { this.cursorPos = cursorPos; } - public @Nullable VColor getCursorColor() { - return cursorColor; - } - public VColor getCursorColorSafe() { - return cursorColor == null ? font.getColor() : cursorColor; - } + VColor style = getStyleOrDefault("cursor-color", null); - public void setCursorColor(@Nullable VColor cursorColor) { - this.cursorColor = cursorColor; + return style == null ? font.getColor() : style; } @Override From a7616968ee3499b359213bb61bd4a2f83d399f59 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 21:47:58 +0200 Subject: [PATCH 131/661] remove all border references --- .../net/snackbag/vera/widget/VWidget.java | 86 ++++--------------- 1 file changed, 17 insertions(+), 69 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index ca3cb588..a0749656 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -4,7 +4,6 @@ import net.snackbag.vera.core.*; import net.snackbag.vera.event.*; import net.snackbag.vera.style.StyleState; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -17,8 +16,6 @@ public abstract class VWidget> { protected int width; protected int height; protected double rotation; - protected V4Color border; - protected V4Int borderSize; protected VeraApp app; public boolean focusOnClick = true; @@ -43,13 +40,15 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { this.rotation = 0; this.eventExecutors = new HashMap<>(); this.visibilityConditions = new ArrayList<>(); - this.border = new V4Color(VColor.black()); - this.borderSize = new V4Int(0); addVisibilityCondition(this::isVisible); setStyle("overlay", VColor.transparent()); setStyle("cursor", VCursorShape.DEFAULT); + + // Border + setStyle("border-color", new V4Color(VColor.black())); + setStyle("border-size", new V4Int(0)); } public abstract void render(); @@ -94,62 +93,6 @@ public void setHeight(int height) { this.height = height; } - public V4Color getBorder() { - return border; - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorder(V4Color border) { - this.border = border; - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorder(VColor all) { - setBorder(new V4Color(all)); - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorder(VColor tb, VColor lr) { - setBorder(new V4Color(tb, lr)); - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorder(VColor top, VColor bottom, VColor left, VColor right) { - setBorder(new V4Color(top, bottom, left, right)); - } - - public V4Int getBorderSize() { - return borderSize; - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorderSize(V4Int borderSize) { - this.borderSize = borderSize; - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorderSize(int all) { - setBorderSize(new V4Int(all)); - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorderSize(int tb, int lr) { - setBorderSize(new V4Int(tb, lr)); - } - - @Deprecated(forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.10.0") - public void setBorderSize(int top, int bottom, int left, int right) { - setBorderSize(new V4Int(top, bottom, left, right)); - } - public void setStyle(String key, Object value) { app.styleSheet.setKey(this, key, value); } @@ -184,28 +127,33 @@ public StyleState createStyleState() { public void renderBorder() { // TODO: [Render Rework] Better border rendering + StyleState state = createStyleState(); + + V4Color borderColor = getStyle("border-color", state); + V4Int borderSize = getStyle("border-size", state); + // Top - Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() - borderSize.get1(), getHitboxWidth(), borderSize.get1(), 0, border.get1()); + Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() - borderSize.get1(), getHitboxWidth(), borderSize.get1(), 0, borderColor.get1()); if (borderSize.get3() > 0) { - Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY() - borderSize.get1(), borderSize.get3(), borderSize.get1(), 0, border.get1()); + Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY() - borderSize.get1(), borderSize.get3(), borderSize.get1(), 0, borderColor.get1()); } // Bottom - Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() + getHitboxHeight(), getHitboxWidth(), borderSize.get2(), 0, border.get2()); + Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() + getHitboxHeight(), getHitboxWidth(), borderSize.get2(), 0, borderColor.get2()); if (borderSize.get4() > 0) { - Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY() + getHitboxHeight(), borderSize.get4(), borderSize.get2(), 0, border.get2()); + Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY() + getHitboxHeight(), borderSize.get4(), borderSize.get2(), 0, borderColor.get2()); } // Left - Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY(), borderSize.get3(), getHitboxHeight(), 0, border.get3()); + Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY(), borderSize.get3(), getHitboxHeight(), 0, borderColor.get3()); if (borderSize.get2() > 0) { - Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY() + getHitboxHeight(), borderSize.get3(), borderSize.get2(), 0, border.get3()); + Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY() + getHitboxHeight(), borderSize.get3(), borderSize.get2(), 0, borderColor.get3()); } // Right - Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY(), borderSize.get4(), getHitboxHeight(), 0, border.get4()); + Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY(), borderSize.get4(), getHitboxHeight(), 0, borderColor.get4()); if (borderSize.get1() > 0) { - Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY() - borderSize.get1(), borderSize.get4(), borderSize.get1(), 0, border.get4()); + Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY() - borderSize.get1(), borderSize.get4(), borderSize.get1(), 0, borderColor.get4()); } } From 1aae56072a5053b81611b2f3f55aacca9de8814d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 21:48:08 +0200 Subject: [PATCH 132/661] move color and clean up font --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index c4af4010..bf0217d5 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -17,6 +17,7 @@ public VStyleSheet() { // Generic reserveType("overlay", StyleValueType.COLOR); reserveType("background-color", StyleValueType.COLOR); + reserveType("color", StyleValueType.COLOR); reserveType("src", StyleValueType.IDENTIFIER); reserveType("cursor", StyleValueType.CURSOR); @@ -28,10 +29,7 @@ public VStyleSheet() { reserveType("color-cursor", StyleValueType.COLOR); // Font - reserveType("color", StyleValueType.COLOR); reserveType("font", StyleValueType.FONT); - reserveType("font-size", StyleValueType.INT); - reserveType("font-name", StyleValueType.STRING); } public T getKey(VWidget widget, String key) { From b96fa52e09638696e16b899b0877dd8d56a517ab Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 21:54:43 +0200 Subject: [PATCH 133/661] add README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..09e2aff0 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Vera + +Vera is a simple yet powerful Fabric UI library. There are currently no plans for a Forge version.\ +[Submit a Logo](https://github.com/snackbag/vera/issues/new) [Visit Wiki](https://wiki.snackbag.net/w/vera) \ No newline at end of file From dc83041399f649a477d7958f7dcb321f32e31608 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:25:19 +0200 Subject: [PATCH 134/661] correctly convert and get svt --- .../java/net/snackbag/vera/style/StyleValueType.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 99eb904a..ebefafae 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -32,8 +32,8 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { return STRING; } - else if (val instanceof V4Color || val instanceof VColor[]) return V4COLOR; - else if (val instanceof V4Int || val instanceof int[]) return V4INT; + else if (val instanceof V4Color || (bias == V4COLOR && (val instanceof VColor[] || val instanceof VColor))) return V4COLOR; + else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val instanceof Integer[] || val instanceof Integer))) return V4INT; else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof Integer) return INT; @@ -49,7 +49,9 @@ public static Object convert(Object value, StyleValueType to) { else if (to == CURSOR) return EnumUtils.getEnumIgnoreCase(VCursorShape.class, v); } - else if (value instanceof int[] v) { + else if (value instanceof int[] || value instanceof Integer[]) { + Integer[] v = (Integer[]) value; + return switch (v.length) { case 1 -> new V4Int(v[0]); case 2 -> new V4Int(v[0], v[1]); @@ -58,6 +60,8 @@ else if (value instanceof int[] v) { }; } + else if (value instanceof Integer v && to == V4INT) return new V4Int(v); + else if (value instanceof VColor[] v) { return switch (v.length) { case 1 -> new V4Color(v[0]); @@ -67,6 +71,8 @@ else if (value instanceof VColor[] v) { }; } + else if (value instanceof VColor v && to == V4COLOR) return new V4Color(v); + else if (to == FLOAT && value instanceof Double v) return v.floatValue(); return value; } From 1a165b1d1d387604397a17eac69eb2b9322a17d2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:25:28 +0200 Subject: [PATCH 135/661] no more removal suppression --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 10f40eda..a34d0c18 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -20,7 +20,6 @@ public TestApplication() { } @Override - @SuppressWarnings("removal") public void init() { VShortcut exit = new VShortcut(this, "escape", () -> { if (hasFocusedWidget()) { From 360151818814448a9392a84e27f406673e730a0d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:25:40 +0200 Subject: [PATCH 136/661] allow arrays --- src/main/java/net/snackbag/vera/widget/VWidget.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index a0749656..23ea2b5e 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -93,11 +93,13 @@ public void setHeight(int height) { this.height = height; } - public void setStyle(String key, Object value) { + @SuppressWarnings("unchecked") + public void setStyle(String key, V... value) { app.styleSheet.setKey(this, key, value); } - public void setStyle(String key, Object value, StyleState state) { + @SuppressWarnings("unchecked") + public void setStyle(String key, StyleState state, V... value) { app.styleSheet.setKey(this, key, value, state); } From 89d9bfe6f2869dccf24d0e4829cd2bdf06560b98 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:25:59 +0200 Subject: [PATCH 137/661] new argument order --- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 2 +- src/main/java/net/snackbag/vera/widget/VDropdown.java | 2 +- src/main/java/net/snackbag/vera/widget/VLineInput.java | 2 +- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 2e81db5c..74548233 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -33,7 +33,7 @@ public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTextu setStyle("src-checked", checkedTexture); setStyle("overlay", VColor.transparent()); - setStyle("cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index c7bb187e..258823bc 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -32,7 +32,7 @@ public VDropdown(VeraApp app) { itemHoverColor = VColor.white().sub(30); padding = new V4Int(5, 10); - setStyle("cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 23d7bb62..e0f492cf 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -38,7 +38,7 @@ public VLineInput(VeraApp app) { setStyle("background-color", VColor.transparent()); this.padding = new V4Int(4); - setStyle("cursor", VCursorShape.TEXT, StyleState.HOVERED); + setStyle("cursor", StyleState.HOVERED, VCursorShape.TEXT); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index a5d2db71..c0f223b2 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -30,7 +30,7 @@ public VTabWidget(VeraApp app, String... tabs) { selectedBackgroundColor = VColor.white(); defaultBackgroundColor = VColor.white().sub(40); - setStyle("cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); } @Override From ba427c445f5b22da87f2c15d7ade7fe40b1a9d07 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:26:08 +0200 Subject: [PATCH 138/661] new argument order --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index a34d0c18..db6e0851 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -94,7 +94,7 @@ public void init() { image.move(0, 30); image.onMiddleClick(this::hideCursor); image.onMiddleClickRelease(this::showCursor); - image.setStyle("src", "minecraft:textures/block/diamond_block.png", StyleState.HOVERED); + image.setStyle("src", StyleState.HOVERED, "minecraft:textures/block/diamond_block.png"); VDropdown dropdown = new VDropdown(this).alsoAdd(); dropdown.addItem("coolio"); @@ -111,7 +111,7 @@ public void init() { VCheckBox checkbox = new VCheckBox(this).alsoAdd(); checkbox.move(20, 140); - checkbox.setStyle("overlay", VColor.white().withOpacity(0.4f), StyleState.HOVERED); + checkbox.setStyle("overlay", StyleState.HOVERED, VColor.white().withOpacity(0.4f)); checkbox.onCheckStateChange((state) -> { if (!state) removeWidget(checkbox); From 03fd0d1e13df984acd512847ae2b23b4a078201d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:26:14 +0200 Subject: [PATCH 139/661] fix borders --- .../java/net/snackbag/mcvera/test/TestApplication.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index db6e0851..240e629f 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -75,8 +75,8 @@ public void init() { centerLabel.setStyle("background-color", VColor.black()); centerLabel.modifyFontColor("font").rgb(255, 255, 255); centerLabel.move(220, 10); - centerLabel.setBorder(VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); - centerLabel.setBorderSize(5, 10, 8, 16); + centerLabel.setStyle("border-color", VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); + centerLabel.setStyle("border-size", 5, 10, 8, 16); centerLabel.setStyle("cursor", VCursorShape.ALL_RESIZE); VLabel rightLabel = new VLabel("RIGHT", this).alsoAdd(); @@ -84,8 +84,8 @@ public void init() { rightLabel.setStyle("background-color", VColor.black()); rightLabel.modifyFontColor("font").rgb(255, 255, 255); rightLabel.move(100, 10); - rightLabel.setBorder(VColor.white()); - rightLabel.setBorderSize(1); + rightLabel.setStyle("border-color", VColor.white()); + rightLabel.setStyle("border-size", 1); rightLabel.onRightClick(() -> System.out.println(Vera.openFileSelector("test", Path.of("/Volumes/Media"), null))); VImage image = new VImage( From ac8151fa1e4b4677a516468fb9c7947ce117b70c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:26:24 +0200 Subject: [PATCH 140/661] reserve border styles --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index bf0217d5..a6b37e89 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -21,6 +21,10 @@ public VStyleSheet() { reserveType("src", StyleValueType.IDENTIFIER); reserveType("cursor", StyleValueType.CURSOR); + // Border + reserveType("border-color", StyleValueType.V4COLOR); + reserveType("border-size", StyleValueType.V4INT); + // Checkbox reserveType("src-checked", StyleValueType.IDENTIFIER); From 3d1d28538b0a1ff90d493da72b77cf6b6db54211 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:26:43 +0200 Subject: [PATCH 141/661] convert arrays of len 1 to non-arrays --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index a6b37e89..79bb19df 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -5,6 +5,7 @@ import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Array; import java.util.HashMap; import java.util.LinkedHashSet; @@ -64,6 +65,10 @@ public void setKey(VWidget widget, String key, Object object) { public void setKey(VWidget widget, String key, Object value, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; + if (value.getClass().isArray()) { + int length = Array.getLength(value); + if (length == 1) value = Array.get(value, 0); + } StyleValueType res = getReservation(key); StyleValueType valRes = StyleValueType.get(value, res); From 4faf81ec2b03886a0c14afaf19f10acc78ab92c2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:55:02 +0200 Subject: [PATCH 142/661] test rect --- .../snackbag/mcvera/test/StyleTestApplication.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 5d61512e..a655f846 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -1,10 +1,12 @@ package net.snackbag.mcvera.test; -import net.minecraft.util.Identifier; -import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.VLabel; +import net.snackbag.vera.widget.VRect; public class StyleTestApplication extends VeraApp { public static final StyleTestApplication INSTANCE = new StyleTestApplication(); @@ -16,5 +18,13 @@ public void init() { //loadStyleSheet(new Identifier(MinecraftVera.MOD_ID, "demo/test.vss")); new VLabel("helo", this).alsoAdd(); + + VRect testRect = new VRect(VColor.black(), this).alsoAdd(); + testRect.setStyle("color", StyleState.HOVERED, VColor.white()); + + testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); + testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); + + testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); } } From 5282025a722a92accb485ddbeaef96b8eec13d93 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:55:32 +0200 Subject: [PATCH 143/661] add getWidgetsReversed --- src/main/java/net/snackbag/vera/core/VeraApp.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 1d9d1f45..44c74208 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -186,6 +186,13 @@ public List> getWidgets() { return new ArrayList<>(widgets); } + public List> getWidgetsReversed() { + List> widgets = getWidgets(); + Collections.reverse(widgets); + + return widgets; + } + public void addWidget(VWidget widget) { this.widgets.add(widget); } @@ -239,7 +246,7 @@ public List getShortcuts() { int mx = px - x; int my = py - y; - return getWidgets().parallelStream() + return getWidgetsReversed().parallelStream() .filter(widget -> isPointOverWidget(widget, mx, my)) .filter(VWidget::visibilityConditionsPassed) .findFirst().orElse(null); From 1cd71ae86033f05ecbdfc44e9e997bfb3da1d5fd Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:55:46 +0200 Subject: [PATCH 144/661] correcly handle click events with style state --- src/main/java/net/snackbag/vera/widget/VWidget.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 23ea2b5e..67d72cfa 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -122,7 +122,13 @@ public V getStyleOrDefault(String key, V dflt, StyleState state) { } public StyleState createStyleState() { - if (isHovered()) return StyleState.HOVERED; + // Clicks first + if (leftClickDown) return StyleState.LEFT_CLICKED; + else if (middleClickDown) return StyleState.MIDDLE_CLICKED; + else if (rightClickDown) return StyleState.RIGHT_CLICKED; + + // Hover as last, since everything else is hover too + else if (isHovered()) return StyleState.HOVERED; else return StyleState.DEFAULT; } From 47118475093976d9e21fa904e738b8e85e3b56af Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 20 Jun 2025 22:55:58 +0200 Subject: [PATCH 145/661] correctly update widget on events --- src/main/java/net/snackbag/vera/widget/VWidget.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 67d72cfa..017504b8 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -363,6 +363,7 @@ public void clearEventsFor(String event) { public void handleBuiltinEvent(String event, Object... args) { StyleState state = createStyleState(); if (state != prevStyleState) update(); + prevStyleState = state; switch (event) { case "left-click" -> { @@ -387,6 +388,8 @@ public void handleBuiltinEvent(String event, Object... args) { } } + state = createStyleState(); + if (state != prevStyleState) update(); prevStyleState = state; } From b1958db1fdf12b11970453c54d0c8a8d611d79bb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 21 Jun 2025 22:04:10 +0200 Subject: [PATCH 146/661] clean up imports --- .../java/net/snackbag/mcvera/mixin/GameRendererMixin.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java index 1fa3ab0c..e86e3e00 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java @@ -1,13 +1,8 @@ package net.snackbag.mcvera.mixin; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.render.GameRenderer; -import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VWindowPositioningFlag; -import org.lwjgl.opengl.GL11; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; From 6afc4a04bef89c7ab70ddedb76294d6242f7fb8b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 21 Jun 2025 22:04:14 +0200 Subject: [PATCH 147/661] fix rounding --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 9db8ca21..2f7bfc74 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -28,8 +28,8 @@ public void drawRect(VeraApp app, int x, int y, int width, int height, double ro MatrixStack stack = drawContext.getMatrices(); stack.push(); - int centerX = x + width / 2; - int centerY = y + height / 2; + float centerX = x + width / 2f; + float centerY = y + height / 2f; stack.translate(app.getX(), app.getY(), 0); stack.translate(centerX, centerY, 0); From e4e927e637566b242ba5e83f40c81b344630ff60 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Jun 2025 17:53:15 +0200 Subject: [PATCH 148/661] add layout testing --- .../net/snackbag/mcvera/InternalCommands.java | 12 +++++++--- .../mcvera/test/LayoutTestApplication.java | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java diff --git a/src/main/java/net/snackbag/mcvera/InternalCommands.java b/src/main/java/net/snackbag/mcvera/InternalCommands.java index cff43a31..0322dcb1 100644 --- a/src/main/java/net/snackbag/mcvera/InternalCommands.java +++ b/src/main/java/net/snackbag/mcvera/InternalCommands.java @@ -5,6 +5,7 @@ import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.command.CommandRegistryAccess; +import net.snackbag.mcvera.test.LayoutTestApplication; import net.snackbag.mcvera.test.StyleTestApplication; import net.snackbag.mcvera.test.TestApplication; @@ -16,15 +17,20 @@ public static void register( if (!FabricLoader.getInstance().isDevelopmentEnvironment()) return; dispatcher.register( - ClientCommandManager.literal("vera") - .then(ClientCommandManager.literal("test1").executes((ctx) -> { + ClientCommandManager.literal("vera").then(ClientCommandManager.literal("test") + .then(ClientCommandManager.literal("generic").executes((ctx) -> { TestApplication.INSTANCE.show(); return 1; })) - .then(ClientCommandManager.literal("test2").executes((ctx) -> { + .then(ClientCommandManager.literal("styles").executes((ctx) -> { StyleTestApplication.INSTANCE.show(); return 1; })) + .then(ClientCommandManager.literal("layout").executes((ctx) -> { + LayoutTestApplication.INSTANCE.show(); + return 1; + })) + ) ); } } diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java new file mode 100644 index 00000000..c4027ddb --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java @@ -0,0 +1,23 @@ +package net.snackbag.mcvera.test; + +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.widget.VLabel; + +public class LayoutTestApplication extends VeraApp { + public static final LayoutTestApplication INSTANCE = new LayoutTestApplication(); + + @Override + public void init() { + new VShortcut(this, "escape", this::hide).alsoAdd(); + + /* + VLayout layout = new VVLayout(this); + VLabel label = new VLabel("I'm a test", this).alsoAddTo(layout); + + VLayout secondLayout = new VHLayout(this); + VLabel horiz1 = new VLabel("1", this).alsoAddTo(secondLayout); + VLabel horiz2 = new VLabel("2", this).alsoAddTo(secondLayout); + */ + } +} From d3c9c2e94eed7270754d203cc0e96ea1e07470e3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Jun 2025 17:54:09 +0200 Subject: [PATCH 149/661] base layout files --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 4 ++++ src/main/java/net/snackbag/vera/layout/VLayout.java | 4 ++++ src/main/java/net/snackbag/vera/layout/VVLayout.java | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/layout/VHLayout.java create mode 100644 src/main/java/net/snackbag/vera/layout/VLayout.java create mode 100644 src/main/java/net/snackbag/vera/layout/VVLayout.java diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java new file mode 100644 index 00000000..a3455e84 --- /dev/null +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -0,0 +1,4 @@ +package net.snackbag.vera.layout; + +public class VHLayout extends VLayout { +} diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java new file mode 100644 index 00000000..c73e88cc --- /dev/null +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -0,0 +1,4 @@ +package net.snackbag.vera.layout; + +public abstract class VLayout { +} diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java new file mode 100644 index 00000000..92f5642a --- /dev/null +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -0,0 +1,4 @@ +package net.snackbag.vera.layout; + +public class VVLayout extends VLayout { +} From 12d90e193bada5fcab034246dcc91e229e410aa4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Jun 2025 18:33:26 +0200 Subject: [PATCH 150/661] add abstraction layer under widget --- src/main/java/net/snackbag/vera/VElement.java | 116 ++++++++++++ .../net/snackbag/vera/widget/VWidget.java | 175 +++--------------- 2 files changed, 140 insertions(+), 151 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/VElement.java diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java new file mode 100644 index 00000000..5e91083d --- /dev/null +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -0,0 +1,116 @@ +package net.snackbag.vera; + +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.EventHandler; +import net.snackbag.vera.event.VWidgetMessageEvent; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public abstract class VElement { + protected int x; + protected int y; + protected int width; + protected int height; + + public boolean visible; + + public final EventHandler events; + public final VeraApp app; + private final List> visibilityConditions = new ArrayList<>(); + + public VElement(VeraApp app, int x, int y, int width, int height) { + this.app = app; + + this.events = new EventHandler(this); + this.events.preprocessor = this::handleBuiltinEvent; + + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public abstract void render(); + public void handleBuiltinEvent(String name, Object... args) {} + + // + // Visibility + // + + public boolean visibilityConditionsPassed() { + return visibilityConditions.parallelStream().allMatch(Supplier::get); + } + + public void addVisibilityCondition(Supplier condition) { + visibilityConditions.add(condition); + } + + public void hide() { + visible = false; + } + + public void show() { + visible = true; + } + + // + // Events + // + + public void onMessage(VWidgetMessageEvent executor) { + events.register("elem-message", args -> executor.run((VWidgetMessageEvent.Context) args[0])); + } + + public void sendMessage(VElement element, String type, @Nullable Object content) { + events.fireEvent("elem-message", new VWidgetMessageEvent.Context(this, type, content)); + } + + // + // Position & Size + // + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public void move(int both) { + move(both, both); + } + + public void move(int x, int y) { + this.x = x; + this.y = y; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void setWidth(int width) { + setSize(width, height); + } + + public void setHeight(int height) { + setSize(width, height); + } + + public void setSize(int both) { + setSize(both, both); + } + + public void setSize(int width, int height) { + this.width = width; + this.height = height; + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 017504b8..0e111a00 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -1,47 +1,33 @@ package net.snackbag.vera.widget; +import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.*; import net.snackbag.vera.style.StyleState; -import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.util.*; -import java.util.function.Supplier; -public abstract class VWidget> { - protected int x; - protected int y; - protected int width; - protected int height; +public abstract class VWidget> extends VElement { protected double rotation; - protected VeraApp app; public boolean focusOnClick = true; private boolean hovered = false; - private boolean visible = true; private boolean leftClickDown = false; private boolean middleClickDown = false; private boolean rightClickDown = false; private StyleState prevStyleState = StyleState.DEFAULT; - private final HashMap> eventExecutors; - private final List> visibilityConditions; public final LinkedHashSet classes = new LinkedHashSet<>(); public VWidget(int x, int y, int width, int height, VeraApp app) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.app = app; + super(app, x, y, width, height); + this.rotation = 0; - this.eventExecutors = new HashMap<>(); - this.visibilityConditions = new ArrayList<>(); - addVisibilityCondition(this::isVisible); + addVisibilityCondition(() -> visible); setStyle("overlay", VColor.transparent()); setStyle("cursor", VCursorShape.DEFAULT); @@ -51,16 +37,9 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { setStyle("border-size", new V4Int(0)); } + @Override public abstract void render(); - public int getX() { - return x; - } - - public int getY() { - return y; - } - public int getHitboxX() { return getX(); } @@ -69,30 +48,14 @@ public int getHitboxY() { return getY(); } - public int getWidth() { - return width; - } - public int getHitboxWidth() { return getWidth(); } - public int getHeight() { - return height; - } - public int getHitboxHeight() { return getHeight(); } - public void setWidth(int width) { - this.width = width; - } - - public void setHeight(int height) { - this.height = height; - } - @SuppressWarnings("unchecked") public void setStyle(String key, V... value) { app.styleSheet.setKey(this, key, value); @@ -171,24 +134,6 @@ public void renderOverlay() { Vera.renderer.drawRect(app, x, y, width, height, 0, getStyle("overlay", state)); } - public void setSize(int width, int height) { - setWidth(width); - setHeight(height); - } - - public void setSize(int all) { - setSize(all, all); - } - - public void move(int x, int y) { - this.x = x; - this.y = y; - } - - public void move(int both) { - move(both, both); - } - public boolean isLeftClickDown() { return leftClickDown; } @@ -205,10 +150,6 @@ public boolean isAnyMouseButtonDown() { return leftClickDown || middleClickDown || rightClickDown; } - public VeraApp getApp() { - return app; - } - public double getRotation() { return rotation; } @@ -230,136 +171,76 @@ public boolean isHovered() { public void setHovered(boolean hovered) { // If changed if (this.hovered != hovered) { - if (hovered) fireEvent("hover"); - else fireEvent("hover-leave"); + if (hovered) events.fireEvent("hover"); + else events.fireEvent("hover-leave"); } this.hovered = hovered; } public void onHover(Runnable runnable) { - registerEventExecutor("hover", runnable); + events.register("hover", runnable); } public void onHoverLeave(Runnable runnable) { - registerEventExecutor("hover-leave", runnable); + events.register("hover-leave", runnable); } public void onLeftClick(Runnable runnable) { - registerEventExecutor("left-click", runnable); + events.register("left-click", runnable); } public void onLeftClickRelease(Runnable runnable) { - registerEventExecutor("left-click-release", runnable); + events.register("left-click-release", runnable); } public void onRightClick(Runnable runnable) { - registerEventExecutor("right-click", runnable); + events.register("right-click", runnable); } public void onRightClickRelease(Runnable runnable) { - registerEventExecutor("right-click-release", runnable); + events.register("right-click-release", runnable); } public void onMiddleClick(Runnable runnable) { - registerEventExecutor("middle-click", runnable); + events.register("middle-click", runnable); } public void onMiddleClickRelease(Runnable runnable) { - registerEventExecutor("middle-click-release", runnable); + events.register("middle-click-release", runnable); } public void onMouseScroll(VMouseScrollEvent runnable) { - registerEventExecutor("mouse-scroll", args -> runnable.run( + events.register("mouse-scroll", args -> runnable.run( (int) args[0], (int) args[1], (double) args[2]) ); } public void onMouseMove(VMouseMoveEvent runnable) { - registerEventExecutor("mouse-move", args -> runnable.run((int) args[0], (int) args[1])); + events.register("mouse-move", args -> runnable.run((int) args[0], (int) args[1])); } public void onMouseDragLeft(VMouseDragEvent runnable) { - registerEventExecutor("mouse-drag-left", args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register("mouse-drag-left", args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragRight(VMouseDragEvent runnable) { - registerEventExecutor("mouse-drag-right", args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register("mouse-drag-right", args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragMiddle(VMouseDragEvent runnable) { - registerEventExecutor("mouse-drag-middle", args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register("mouse-drag-middle", args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onFocusStateChange(Runnable runnable) { - registerEventExecutor("focus-state-change", runnable); + events.register("focus-state-change", runnable); } public void onFilesDropped(VFilesDroppedEvent runnable) { - registerEventExecutor("files-dropped", args -> runnable.run((List) args[0])); - } - - public void onMessage(VWidgetMessageEvent runnable) { - registerEventExecutor("widget-message", args -> runnable.run((VWidgetMessageEvent.Context) args[0])); - } - - public void sendMessage(VWidget widget, String type) { - sendMessage(widget, type, null); - } - - public void sendMessage(VWidget widget, String type, @Nullable Object content) { - widget.fireEvent("widget-message", new VWidgetMessageEvent.Context(this, type, content)); - } - - public void sendMessageAll(String type) { - sendMessageAll(type, null); - } - - public void sendMessageAll(String type, @Nullable Object content) { - VWidgetMessageEvent.Context ctx = new VWidgetMessageEvent.Context(this, type, content); - for (VWidget widget : app.getWidgets()) widget.fireEvent("widget-message", ctx); - } - - public boolean isVisible() { - return visible; - } - - public void setVisible(boolean visible) { - this.visible = visible; - } - - public void show() { - setVisible(true); - } - - public void hide() { - setVisible(false); - } - - public void registerEventExecutor(String event, VEvent executor) { - eventExecutors.computeIfAbsent(event, k -> new ArrayList<>()).add(executor); - } - - public void registerEventExecutor(String event, Runnable runnable) { - registerEventExecutor(event, args -> runnable.run()); - } - - public void fireEvent(String event, Object... args) { - handleBuiltinEvent(event, args); - - if (!eventExecutors.containsKey(event)) return; - eventExecutors.get(event).parallelStream().forEach(e -> e.run(args)); - } - - public void clearEvents() { - eventExecutors.clear(); - } - - public void clearEventsFor(String event) { - // IDE said I don't need a containsKey check - eventExecutors.remove(event); + events.register("files-dropped", args -> runnable.run((List) args[0])); } + @Override public void handleBuiltinEvent(String event, Object... args) { StyleState state = createStyleState(); if (state != prevStyleState) update(); @@ -426,12 +307,4 @@ public T alsoAdd() { app.addWidget(this); return (T) this; } - - public void addVisibilityCondition(Supplier condition) { - visibilityConditions.add(condition); - } - - public boolean visibilityConditionsPassed() { - return visibilityConditions.parallelStream().allMatch(Supplier::get); - } } From 2a82f78c5dfc21a0ed71d03431eae9bb38624971 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Jun 2025 18:33:32 +0200 Subject: [PATCH 151/661] rework events --- .../net/snackbag/vera/event/EventHandler.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/event/EventHandler.java diff --git a/src/main/java/net/snackbag/vera/event/EventHandler.java b/src/main/java/net/snackbag/vera/event/EventHandler.java new file mode 100644 index 00000000..db317fff --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/EventHandler.java @@ -0,0 +1,47 @@ +package net.snackbag.vera.event; + +import net.snackbag.vera.VElement; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class EventHandler { + public final VElement element; + public @Nullable PreProcessorContext preprocessor; + + private final HashMap> executors = new HashMap<>(); + + public EventHandler(VElement element) { + this.element = element; + } + + public void fireEvent(String name, Object... args) { + if (preprocessor != null) preprocessor.call(name, args); + + if (!executors.containsKey(name)) return; + executors.get(name).parallelStream().forEach(e -> e.run(args)); + } + + public void register(String name, VEvent executor) { + executors.computeIfAbsent(name, k -> new ArrayList<>()).add(executor); + } + + public void register(String name, Runnable executor) { + register(name, e -> executor.run()); + } + + public void clear() { + executors.clear(); + } + + public void clear(String name) { + executors.remove(name); + } + + @FunctionalInterface + public interface PreProcessorContext { + void call(String name, Object[] args); + } +} From bf5db14d835b3fda1f2f47559fb891155c6ebe03 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Jun 2025 18:33:44 +0200 Subject: [PATCH 152/661] rework VWidgetMessageEvent.Context --- .../vera/event/VWidgetMessageEvent.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/snackbag/vera/event/VWidgetMessageEvent.java b/src/main/java/net/snackbag/vera/event/VWidgetMessageEvent.java index 39050b1e..ec13ab00 100644 --- a/src/main/java/net/snackbag/vera/event/VWidgetMessageEvent.java +++ b/src/main/java/net/snackbag/vera/event/VWidgetMessageEvent.java @@ -1,6 +1,6 @@ package net.snackbag.vera.event; -import net.snackbag.vera.widget.VWidget; +import net.snackbag.vera.VElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -8,27 +8,17 @@ public interface VWidgetMessageEvent { void run(Context ctx); - class Context { - public final @NotNull VWidget sender; - public final @NotNull String type; - public final @Nullable Object content; - - public Context(@NotNull VWidget sender, @NotNull String type, @Nullable Object content) { - this.sender = sender; - this.type = type; - this.content = content; - } - + record Context(@NotNull VElement sender, @NotNull String type, @Nullable Object content) { public boolean isContentNull() { - return content == null; - } + return content == null; + } - public boolean isContentString() { - return content != null && content instanceof String; - } + public boolean isContentString() { + return content != null && content instanceof String; + } - public T getContentOrDefault(T default_) { - return content == null ? default_ : (T) content; + public T getContentOrDefault(T default_) { + return content == null ? default_ : (T) content; + } } - } } From b4dad9fc00979ee89895c38d530ed7d92ff3dae1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Jun 2025 18:38:56 +0200 Subject: [PATCH 153/661] update to new widget event system --- .../snackbag/mcvera/impl/MCVeraProvider.java | 4 +- .../net/snackbag/mcvera/mixin/MouseMixin.java | 2 +- .../mcvera/mixin/ParentElementMixin.java | 14 ++--- .../java/net/snackbag/vera/core/VeraApp.java | 12 ++-- .../net/snackbag/vera/widget/VCheckBox.java | 4 +- .../net/snackbag/vera/widget/VDropdown.java | 12 ++-- .../java/net/snackbag/vera/widget/VImage.java | 2 - .../java/net/snackbag/vera/widget/VLabel.java | 2 - .../net/snackbag/vera/widget/VLineInput.java | 58 +++++++++---------- .../net/snackbag/vera/widget/VTabWidget.java | 29 +++++----- 10 files changed, 67 insertions(+), 72 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index 7df7d5f2..1f5e03aa 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -134,7 +134,7 @@ public void handleFilesDropped(List paths) { if (top != null && top.isPointOverThis(x, y)) { VWidget widget = top.getTopWidgetAt(x, y); if (widget != null) { - widget.fireEvent("files-dropped", paths); + widget.events.fireEvent("files-dropped", paths); return; } } @@ -146,7 +146,7 @@ public void handleFilesDropped(List paths) { VWidget widget = app.getTopWidgetAt(x, y); if (widget != null) { - widget.fireEvent("files-dropped", paths); + widget.events.fireEvent("files-dropped", paths); didSomething.set(true); } }); diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index da5dd9b4..cd6680f5 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -35,7 +35,7 @@ public abstract class MouseMixin { if (app.isRequiresHierarchy() && app != top) return; VWidget widget = app.getTopWidgetAt(mouseX, mouseY); - if (widget != null) widget.fireEvent("mouse-move", mouseX, mouseY); + if (widget != null) widget.events.fireEvent("mouse-move", mouseX, mouseY); else if (app.getCursorShape() != VCursorShape.DEFAULT) app.setCursorShape(VCursorShape.DEFAULT); }); } diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index dc2c65e7..405312f3 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -52,9 +52,9 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { if (widget == null) return; switch (button) { - case 0 -> widget.fireEvent("left-click"); - case 1 -> widget.fireEvent("right-click"); - case 2 -> widget.fireEvent("middle-click"); + case 0 -> widget.events.fireEvent("left-click"); + case 1 -> widget.events.fireEvent("right-click"); + case 2 -> widget.events.fireEvent("middle-click"); default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); } } @@ -78,9 +78,9 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { if (widget == null) return; switch (button) { - case 0 -> widget.fireEvent("left-click-release"); - case 1 -> widget.fireEvent("right-click-release"); - case 2 -> widget.fireEvent("middle-click-release"); + case 0 -> widget.events.fireEvent("left-click-release"); + case 1 -> widget.events.fireEvent("right-click-release"); + case 2 -> widget.events.fireEvent("middle-click-release"); default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); } } @@ -102,6 +102,6 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { @Unique private void handleScrollEvents(@Nullable VWidget widget, int x, int y, double amount) { if (widget == null) return; - widget.fireEvent("mouse-scroll", x, y, amount); + widget.events.fireEvent("mouse-scroll", x, y, amount); } } diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 44c74208..20f922a7 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -201,10 +201,10 @@ public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; if (isFocusedWidget(widget)) setFocusedWidget(null); - if (widget.isLeftClickDown()) widget.fireEvent("left-click-release"); - if (widget.isMiddleClickDown()) widget.fireEvent("middle-click-release"); - if (widget.isRightClickDown()) widget.fireEvent("right-click-release"); - if (widget.isHovered()) widget.fireEvent("hover-leave"); + if (widget.isLeftClickDown()) widget.events.fireEvent("left-click-release"); + if (widget.isMiddleClickDown()) widget.events.fireEvent("middle-click-release"); + if (widget.isRightClickDown()) widget.events.fireEvent("right-click-release"); + if (widget.isHovered()) widget.events.fireEvent("hover-leave"); this.widgets.remove(widget); } @@ -273,8 +273,8 @@ public void setFocusedWidget(@Nullable VWidget widget) { VWidget oldWidget = this.focusedWidget; this.focusedWidget = widget; - if (oldWidget != null) oldWidget.fireEvent("focus-state-change"); - if (widget != null) widget.fireEvent("focus-state-change"); + if (oldWidget != null) oldWidget.events.fireEvent("focus-state-change"); + if (widget != null) widget.events.fireEvent("focus-state-change"); } } diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 74548233..cdeec165 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -57,10 +57,10 @@ public boolean isChecked() { public void setChecked(boolean checked) { this.checked = checked; - fireEvent("vcheckbox-checked", checked); + events.fireEvent("vcheckbox-checked", checked); } public void onCheckStateChange(VCheckedStateChange runnable) { - registerEventExecutor("vcheckbox-checked", args -> runnable.run((boolean) args[0])); + events.register("vcheckbox-checked", args -> runnable.run((boolean) args[0])); } } diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 258823bc..61d037ff 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -84,8 +84,8 @@ app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), public void setFocused(boolean focused) { super.setFocused(focused); - if (focused) fireEvent("vdropdown-selector-open"); - else fireEvent("vdropdown-selector-close"); + if (focused) events.fireEvent("vdropdown-selector-open"); + else events.fireEvent("vdropdown-selector-close"); } public VColor getItemHoverColor() { @@ -221,15 +221,15 @@ private int getItemIndexAt(int mouseY) { } public void onItemSwitch(VItemSwitchEvent runnable) { - registerEventExecutor("vdropdown-item-switch", args -> runnable.run((int) args[0])); + events.register("vdropdown-item-switch", args -> runnable.run((int) args[0])); } public void onSelectorOpen(Runnable runnable) { - registerEventExecutor("vdropdown-selector-open", runnable); + events.register("vdropdown-selector-open", runnable); } public void onSelectorClose(Runnable runnable) { - registerEventExecutor("vdropdown-selector-close", runnable); + events.register("vdropdown-selector-close", runnable); } private @Nullable Item getItemAt(int mouseX, int mouseY) { @@ -262,7 +262,7 @@ public int getSelectedItem() { public void setSelectedItem(int selectedItem) { this.selectedItem = selectedItem; - fireEvent("vdropdown-item-switch", selectedItem); + events.fireEvent("vdropdown-item-switch", selectedItem); } public VFont getFont() { diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 5a74fdca..f044803d 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -19,8 +19,6 @@ public VImage(String src, int width, int height, VeraApp app) { @Override public void render() { - VeraApp app = getApp(); - StyleState state = createStyleState(); Identifier src = getStyle("src", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index d17894a4..ec0c4451 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -90,8 +90,6 @@ public VColor.ColorModifier modifyFontColor(String key) { @Override public void render() { - VeraApp app = getApp(); - StyleState state = createStyleState(); VFont font = getStyle("font", state); VColor backgroundColor = getStyle("background-color", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index e0f492cf..2d572e36 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -143,7 +143,7 @@ public String getText() { public void setText(String text) { this.text = text; - fireEvent("vline-change"); + events.fireEvent("vline-change"); } public boolean isSelectingText() { @@ -188,23 +188,23 @@ public String getPlaceholderText() { } public void onLineChanged(Runnable runnable) { - registerEventExecutor("vline-change", runnable); + events.register("vline-change", runnable); } public void onCursorMove(Runnable runnable) { - registerEventExecutor("vline-cursor-move", runnable); + events.register("vline-cursor-move", runnable); } public void onCursorMoveLeft(Runnable runnable) { - registerEventExecutor("vline-cursor-move-left", runnable); + events.register("vline-cursor-move-left", runnable); } public void onCursorMoveRight(Runnable runnable) { - registerEventExecutor("vline-cursor-move-right", runnable); + events.register("vline-cursor-move-right", runnable); } public void onAddCharLimited(VCharLimitedEvent runnable) { - registerEventExecutor("vline-add-char-limited", args -> runnable.run((char) args[0])); + events.register("vline-add-char-limited", args -> runnable.run((char) args[0])); } @Override @@ -289,32 +289,32 @@ else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && cursorPos > 0) { // Handle word navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isAltDown() && cursorPos > 0) { cursorPos = Math.max(0, jumpToWordStart(cursorPos)); - fireEvent("vline-cursor-move"); - fireEvent("vline-cursor-move-left"); + events.fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move-left"); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isAltDown() && cursorPos < text.length()) { cursorPos = Math.min(text.length(), jumpToWordEnd(cursorPos)); - fireEvent("vline-cursor-move"); - fireEvent("vline-cursor-move-right"); + events.fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move-right"); } // Handle line navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isCtrlDown()) { cursorPos = 0; - fireEvent("vline-cursor-move"); - fireEvent("vline-cursor-move-left"); + events.fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move-left"); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isCtrlDown()) { cursorPos = text.length(); - fireEvent("vline-cursor-move"); - fireEvent("vline-cursor-move-right"); + events.fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move-right"); } // Handle character navigation else if (keyCode == GLFW.GLFW_KEY_LEFT && cursorPos > 0) { cursorPos = Math.max(0, cursorPos - 1); - fireEvent("vline-cursor-move"); - fireEvent("vline-cursor-move-left"); + events.fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move-left"); } else if (keyCode == GLFW.GLFW_KEY_RIGHT && cursorPos < text.length()) { cursorPos = Math.min(text.length(), cursorPos + 1); - fireEvent("vline-cursor-move"); - fireEvent("vline-cursor-move-right"); + events.fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move-right"); } super.keyPressed(keyCode, scanCode, modifiers); @@ -346,12 +346,12 @@ private void handleSelectionKeyPress(int keyCode) { cursorPos = newPos; textSelection.endPos = newPos; - fireEvent("vline-cursor-move"); + events.fireEvent("vline-cursor-move"); } private void insertText(String insertion) { if (maxChars > -1 && text.length() + insertion.length() > maxChars) { - fireEvent("vline-add-char-limited", insertion.charAt(0)); + events.fireEvent("vline-add-char-limited", insertion.charAt(0)); return; } @@ -359,7 +359,7 @@ private void insertText(String insertion) { String back = text.substring(cursorPos); text = front + insertion + back; cursorPos += insertion.length(); - fireEvent("vline-change"); + events.fireEvent("vline-change"); } private void deleteSelectedText() { @@ -373,7 +373,7 @@ private void deleteSelectedText() { text = front + back; cursorPos = start; clearTextSelection(); - fireEvent("vline-change"); + events.fireEvent("vline-change"); } private void replaceSelectedText(String replacement) { @@ -383,7 +383,7 @@ private void replaceSelectedText(String replacement) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + replacement.length() > maxChars) { - fireEvent("vline-add-char-limited", replacement.charAt(0)); + events.fireEvent("vline-add-char-limited", replacement.charAt(0)); return; } @@ -392,7 +392,7 @@ private void replaceSelectedText(String replacement) { text = front + replacement + back; cursorPos = start + replacement.length(); clearTextSelection(); - fireEvent("vline-change"); + events.fireEvent("vline-change"); } @@ -457,7 +457,7 @@ public void charTyped(char chr, int modifiers) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + 1 > maxChars) { - fireEvent("vline-add-char-limited", chr); + events.fireEvent("vline-add-char-limited", chr); return; } @@ -467,11 +467,11 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos = start + 1; clearTextSelection(); - fireEvent("vline-change"); + events.fireEvent("vline-change"); } else { // Normal character insertion if (maxChars > -1 && text.length() >= maxChars) { - fireEvent("vline-add-char-limited", chr); + events.fireEvent("vline-add-char-limited", chr); return; } @@ -480,7 +480,7 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos += 1; - fireEvent("vline-change"); + events.fireEvent("vline-change"); } } super.charTyped(chr, modifiers); @@ -559,7 +559,7 @@ private void deleteText(int start, int end) { builder.delete(start, end); text = builder.toString(); cursorPos = Math.min(start, text.length()); - fireEvent("vline-change"); + events.fireEvent("vline-change"); } public static class TextSelection { diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index c0f223b2..5891db15 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -1,6 +1,5 @@ package net.snackbag.vera.widget; -import net.minecraft.client.MinecraftClient; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; @@ -67,30 +66,30 @@ public void handleBuiltinEvent(String event, Object... args) { case "left-click" -> { if (!isValidTabIndex(hoveredTab)) return; - fireEvent("vtabwidget-tab-left-click", hoveredTab); + events.fireEvent("vtabwidget-tab-left-click", hoveredTab); setActiveTab(hoveredTab); } case "left-click-release" -> { if (!isValidTabIndex(hoveredTab)) return; - fireEvent("vtabwidget-tab-left-click-release", hoveredTab); + events.fireEvent("vtabwidget-tab-left-click-release", hoveredTab); } case "middle-click" -> { if (!isValidTabIndex(hoveredTab)) return; - fireEvent("vtabwidget-tab-middle-click", hoveredTab); + events.fireEvent("vtabwidget-tab-middle-click", hoveredTab); } case "middle-click-release" -> { if (!isValidTabIndex(hoveredTab)) return; - fireEvent("vtabwidget-tab-middle-click-release", hoveredTab); + events.fireEvent("vtabwidget-tab-middle-click-release", hoveredTab); } case "right-click" -> { if (!isValidTabIndex(hoveredTab)) return; - fireEvent("vtabwidget-tab-right-click", hoveredTab); + events.fireEvent("vtabwidget-tab-right-click", hoveredTab); } case "right-click-release" -> { if (!isValidTabIndex(hoveredTab)) return; - fireEvent("vtabwidget-tab-right-click-release", hoveredTab); + events.fireEvent("vtabwidget-tab-right-click-release", hoveredTab); } } @@ -117,7 +116,7 @@ public int getHoveredTabIndex(int mouseX) { if (relativeX >= currentX && relativeX < currentX + totalTabWidth) { if (hoveredTab != null && hoveredTab != index) { - fireEvent("vtabwidget-tab-hover-change", hoveredTab); + events.fireEvent("vtabwidget-tab-hover-change", hoveredTab); } hoveredTab = index; @@ -136,31 +135,31 @@ public int getHoveredTabIndex(int mouseX) { } public void onTabHoverChange(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-hover-change", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-hover-change", (args) -> runnable.accept((int) args[0])); } public void onTabLeftClick(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-left-click", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-left-click", (args) -> runnable.accept((int) args[0])); } public void onTabLeftClickRelease(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-left-click-release", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-left-click-release", (args) -> runnable.accept((int) args[0])); } public void onTabMiddleClick(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-middle-click", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-middle-click", (args) -> runnable.accept((int) args[0])); } public void onTabMiddleClickRelease(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-middle-click-release", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-middle-click-release", (args) -> runnable.accept((int) args[0])); } public void onTabRightClick(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-right-click", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-right-click", (args) -> runnable.accept((int) args[0])); } public void onTabRightClickRelease(Consumer runnable) { - registerEventExecutor("vtabwidget-tab-right-click-release", (args) -> runnable.accept((int) args[0])); + events.register("vtabwidget-tab-right-click-release", (args) -> runnable.accept((int) args[0])); } public void addTab(String tab, VWidget... widgets) { From 1e667bac2e1adfaaa80914ffd756482706c27a84 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 15:24:11 +0200 Subject: [PATCH 154/661] fireEvent -> fire --- .../snackbag/mcvera/impl/MCVeraProvider.java | 4 +- .../net/snackbag/mcvera/mixin/MouseMixin.java | 3 +- .../mcvera/mixin/ParentElementMixin.java | 14 +++--- src/main/java/net/snackbag/vera/VElement.java | 2 +- .../java/net/snackbag/vera/core/VeraApp.java | 12 ++--- .../net/snackbag/vera/event/EventHandler.java | 2 +- .../net/snackbag/vera/widget/VCheckBox.java | 2 +- .../net/snackbag/vera/widget/VDropdown.java | 6 +-- .../net/snackbag/vera/widget/VLineInput.java | 48 +++++++++---------- .../net/snackbag/vera/widget/VTabWidget.java | 14 +++--- .../net/snackbag/vera/widget/VWidget.java | 4 +- 11 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index 1f5e03aa..df158a9c 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -134,7 +134,7 @@ public void handleFilesDropped(List paths) { if (top != null && top.isPointOverThis(x, y)) { VWidget widget = top.getTopWidgetAt(x, y); if (widget != null) { - widget.events.fireEvent("files-dropped", paths); + widget.events.fire("files-dropped", paths); return; } } @@ -146,7 +146,7 @@ public void handleFilesDropped(List paths) { VWidget widget = app.getTopWidgetAt(x, y); if (widget != null) { - widget.events.fireEvent("files-dropped", paths); + widget.events.fire("files-dropped", paths); didSomething.set(true); } }); diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index cd6680f5..a100815a 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -2,7 +2,6 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.Mouse; -import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; @@ -35,7 +34,7 @@ public abstract class MouseMixin { if (app.isRequiresHierarchy() && app != top) return; VWidget widget = app.getTopWidgetAt(mouseX, mouseY); - if (widget != null) widget.events.fireEvent("mouse-move", mouseX, mouseY); + if (widget != null) widget.events.fire("mouse-move", mouseX, mouseY); else if (app.getCursorShape() != VCursorShape.DEFAULT) app.setCursorShape(VCursorShape.DEFAULT); }); } diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 405312f3..7e1f9e86 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -52,9 +52,9 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { if (widget == null) return; switch (button) { - case 0 -> widget.events.fireEvent("left-click"); - case 1 -> widget.events.fireEvent("right-click"); - case 2 -> widget.events.fireEvent("middle-click"); + case 0 -> widget.events.fire("left-click"); + case 1 -> widget.events.fire("right-click"); + case 2 -> widget.events.fire("middle-click"); default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); } } @@ -78,9 +78,9 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { if (widget == null) return; switch (button) { - case 0 -> widget.events.fireEvent("left-click-release"); - case 1 -> widget.events.fireEvent("right-click-release"); - case 2 -> widget.events.fireEvent("middle-click-release"); + case 0 -> widget.events.fire("left-click-release"); + case 1 -> widget.events.fire("right-click-release"); + case 2 -> widget.events.fire("middle-click-release"); default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); } } @@ -102,6 +102,6 @@ private void handleReleaseEvents(@Nullable VWidget widget, int button) { @Unique private void handleScrollEvents(@Nullable VWidget widget, int x, int y, double amount) { if (widget == null) return; - widget.events.fireEvent("mouse-scroll", x, y, amount); + widget.events.fire("mouse-scroll", x, y, amount); } } diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 5e91083d..db1f0eb5 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -65,7 +65,7 @@ public void onMessage(VWidgetMessageEvent executor) { } public void sendMessage(VElement element, String type, @Nullable Object content) { - events.fireEvent("elem-message", new VWidgetMessageEvent.Context(this, type, content)); + events.fire("elem-message", new VWidgetMessageEvent.Context(this, type, content)); } // diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 20f922a7..08682ab0 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -201,10 +201,10 @@ public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; if (isFocusedWidget(widget)) setFocusedWidget(null); - if (widget.isLeftClickDown()) widget.events.fireEvent("left-click-release"); - if (widget.isMiddleClickDown()) widget.events.fireEvent("middle-click-release"); - if (widget.isRightClickDown()) widget.events.fireEvent("right-click-release"); - if (widget.isHovered()) widget.events.fireEvent("hover-leave"); + if (widget.isLeftClickDown()) widget.events.fire("left-click-release"); + if (widget.isMiddleClickDown()) widget.events.fire("middle-click-release"); + if (widget.isRightClickDown()) widget.events.fire("right-click-release"); + if (widget.isHovered()) widget.events.fire("hover-leave"); this.widgets.remove(widget); } @@ -273,8 +273,8 @@ public void setFocusedWidget(@Nullable VWidget widget) { VWidget oldWidget = this.focusedWidget; this.focusedWidget = widget; - if (oldWidget != null) oldWidget.events.fireEvent("focus-state-change"); - if (widget != null) widget.events.fireEvent("focus-state-change"); + if (oldWidget != null) oldWidget.events.fire("focus-state-change"); + if (widget != null) widget.events.fire("focus-state-change"); } } diff --git a/src/main/java/net/snackbag/vera/event/EventHandler.java b/src/main/java/net/snackbag/vera/event/EventHandler.java index db317fff..6fc9a338 100644 --- a/src/main/java/net/snackbag/vera/event/EventHandler.java +++ b/src/main/java/net/snackbag/vera/event/EventHandler.java @@ -17,7 +17,7 @@ public EventHandler(VElement element) { this.element = element; } - public void fireEvent(String name, Object... args) { + public void fire(String name, Object... args) { if (preprocessor != null) preprocessor.call(name, args); if (!executors.containsKey(name)) return; diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index cdeec165..0634eeed 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -57,7 +57,7 @@ public boolean isChecked() { public void setChecked(boolean checked) { this.checked = checked; - events.fireEvent("vcheckbox-checked", checked); + events.fire("vcheckbox-checked", checked); } public void onCheckStateChange(VCheckedStateChange runnable) { diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 61d037ff..66b4136c 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -84,8 +84,8 @@ app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), public void setFocused(boolean focused) { super.setFocused(focused); - if (focused) events.fireEvent("vdropdown-selector-open"); - else events.fireEvent("vdropdown-selector-close"); + if (focused) events.fire("vdropdown-selector-open"); + else events.fire("vdropdown-selector-close"); } public VColor getItemHoverColor() { @@ -262,7 +262,7 @@ public int getSelectedItem() { public void setSelectedItem(int selectedItem) { this.selectedItem = selectedItem; - events.fireEvent("vdropdown-item-switch", selectedItem); + events.fire("vdropdown-item-switch", selectedItem); } public VFont getFont() { diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 2d572e36..16d44c62 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -143,7 +143,7 @@ public String getText() { public void setText(String text) { this.text = text; - events.fireEvent("vline-change"); + events.fire("vline-change"); } public boolean isSelectingText() { @@ -289,32 +289,32 @@ else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && cursorPos > 0) { // Handle word navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isAltDown() && cursorPos > 0) { cursorPos = Math.max(0, jumpToWordStart(cursorPos)); - events.fireEvent("vline-cursor-move"); - events.fireEvent("vline-cursor-move-left"); + events.fire("vline-cursor-move"); + events.fire("vline-cursor-move-left"); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isAltDown() && cursorPos < text.length()) { cursorPos = Math.min(text.length(), jumpToWordEnd(cursorPos)); - events.fireEvent("vline-cursor-move"); - events.fireEvent("vline-cursor-move-right"); + events.fire("vline-cursor-move"); + events.fire("vline-cursor-move-right"); } // Handle line navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isCtrlDown()) { cursorPos = 0; - events.fireEvent("vline-cursor-move"); - events.fireEvent("vline-cursor-move-left"); + events.fire("vline-cursor-move"); + events.fire("vline-cursor-move-left"); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isCtrlDown()) { cursorPos = text.length(); - events.fireEvent("vline-cursor-move"); - events.fireEvent("vline-cursor-move-right"); + events.fire("vline-cursor-move"); + events.fire("vline-cursor-move-right"); } // Handle character navigation else if (keyCode == GLFW.GLFW_KEY_LEFT && cursorPos > 0) { cursorPos = Math.max(0, cursorPos - 1); - events.fireEvent("vline-cursor-move"); - events.fireEvent("vline-cursor-move-left"); + events.fire("vline-cursor-move"); + events.fire("vline-cursor-move-left"); } else if (keyCode == GLFW.GLFW_KEY_RIGHT && cursorPos < text.length()) { cursorPos = Math.min(text.length(), cursorPos + 1); - events.fireEvent("vline-cursor-move"); - events.fireEvent("vline-cursor-move-right"); + events.fire("vline-cursor-move"); + events.fire("vline-cursor-move-right"); } super.keyPressed(keyCode, scanCode, modifiers); @@ -346,12 +346,12 @@ private void handleSelectionKeyPress(int keyCode) { cursorPos = newPos; textSelection.endPos = newPos; - events.fireEvent("vline-cursor-move"); + events.fire("vline-cursor-move"); } private void insertText(String insertion) { if (maxChars > -1 && text.length() + insertion.length() > maxChars) { - events.fireEvent("vline-add-char-limited", insertion.charAt(0)); + events.fire("vline-add-char-limited", insertion.charAt(0)); return; } @@ -359,7 +359,7 @@ private void insertText(String insertion) { String back = text.substring(cursorPos); text = front + insertion + back; cursorPos += insertion.length(); - events.fireEvent("vline-change"); + events.fire("vline-change"); } private void deleteSelectedText() { @@ -373,7 +373,7 @@ private void deleteSelectedText() { text = front + back; cursorPos = start; clearTextSelection(); - events.fireEvent("vline-change"); + events.fire("vline-change"); } private void replaceSelectedText(String replacement) { @@ -383,7 +383,7 @@ private void replaceSelectedText(String replacement) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + replacement.length() > maxChars) { - events.fireEvent("vline-add-char-limited", replacement.charAt(0)); + events.fire("vline-add-char-limited", replacement.charAt(0)); return; } @@ -392,7 +392,7 @@ private void replaceSelectedText(String replacement) { text = front + replacement + back; cursorPos = start + replacement.length(); clearTextSelection(); - events.fireEvent("vline-change"); + events.fire("vline-change"); } @@ -457,7 +457,7 @@ public void charTyped(char chr, int modifiers) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + 1 > maxChars) { - events.fireEvent("vline-add-char-limited", chr); + events.fire("vline-add-char-limited", chr); return; } @@ -467,11 +467,11 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos = start + 1; clearTextSelection(); - events.fireEvent("vline-change"); + events.fire("vline-change"); } else { // Normal character insertion if (maxChars > -1 && text.length() >= maxChars) { - events.fireEvent("vline-add-char-limited", chr); + events.fire("vline-add-char-limited", chr); return; } @@ -480,7 +480,7 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos += 1; - events.fireEvent("vline-change"); + events.fire("vline-change"); } } super.charTyped(chr, modifiers); @@ -559,7 +559,7 @@ private void deleteText(int start, int end) { builder.delete(start, end); text = builder.toString(); cursorPos = Math.min(start, text.length()); - events.fireEvent("vline-change"); + events.fire("vline-change"); } public static class TextSelection { diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 5891db15..f68a921c 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -66,30 +66,30 @@ public void handleBuiltinEvent(String event, Object... args) { case "left-click" -> { if (!isValidTabIndex(hoveredTab)) return; - events.fireEvent("vtabwidget-tab-left-click", hoveredTab); + events.fire("vtabwidget-tab-left-click", hoveredTab); setActiveTab(hoveredTab); } case "left-click-release" -> { if (!isValidTabIndex(hoveredTab)) return; - events.fireEvent("vtabwidget-tab-left-click-release", hoveredTab); + events.fire("vtabwidget-tab-left-click-release", hoveredTab); } case "middle-click" -> { if (!isValidTabIndex(hoveredTab)) return; - events.fireEvent("vtabwidget-tab-middle-click", hoveredTab); + events.fire("vtabwidget-tab-middle-click", hoveredTab); } case "middle-click-release" -> { if (!isValidTabIndex(hoveredTab)) return; - events.fireEvent("vtabwidget-tab-middle-click-release", hoveredTab); + events.fire("vtabwidget-tab-middle-click-release", hoveredTab); } case "right-click" -> { if (!isValidTabIndex(hoveredTab)) return; - events.fireEvent("vtabwidget-tab-right-click", hoveredTab); + events.fire("vtabwidget-tab-right-click", hoveredTab); } case "right-click-release" -> { if (!isValidTabIndex(hoveredTab)) return; - events.fireEvent("vtabwidget-tab-right-click-release", hoveredTab); + events.fire("vtabwidget-tab-right-click-release", hoveredTab); } } @@ -116,7 +116,7 @@ public int getHoveredTabIndex(int mouseX) { if (relativeX >= currentX && relativeX < currentX + totalTabWidth) { if (hoveredTab != null && hoveredTab != index) { - events.fireEvent("vtabwidget-tab-hover-change", hoveredTab); + events.fire("vtabwidget-tab-hover-change", hoveredTab); } hoveredTab = index; diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 0e111a00..7acb78e5 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -171,8 +171,8 @@ public boolean isHovered() { public void setHovered(boolean hovered) { // If changed if (this.hovered != hovered) { - if (hovered) events.fireEvent("hover"); - else events.fireEvent("hover-leave"); + if (hovered) events.fire("hover"); + else events.fire("hover-leave"); } this.hovered = hovered; From b269a41bddb480056018809971b7c8bf243ff882 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 15:32:14 +0200 Subject: [PATCH 155/661] adjust builtin events super order to fix hover style events --- src/main/java/net/snackbag/vera/VElement.java | 4 +++- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 3 ++- src/main/java/net/snackbag/vera/widget/VDropdown.java | 4 ++-- src/main/java/net/snackbag/vera/widget/VLineInput.java | 4 ++-- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 4 ++-- src/main/java/net/snackbag/vera/widget/VWidget.java | 5 ----- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index db1f0eb5..1c70c0ae 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -15,7 +15,7 @@ public abstract class VElement { protected int width; protected int height; - public boolean visible; + public boolean visible = true; public final EventHandler events; public final VeraApp app; @@ -27,6 +27,8 @@ public VElement(VeraApp app, int x, int y, int width, int height) { this.events = new EventHandler(this); this.events.preprocessor = this::handleBuiltinEvent; + addVisibilityCondition(() -> visible); + this.x = x; this.y = y; this.width = width; diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 0634eeed..263e6deb 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -46,8 +46,9 @@ public void render() { @Override public void handleBuiltinEvent(String event, Object... args) { - if (event.equals("left-click")) setChecked(!checked); super.handleBuiltinEvent(event, args); + + if (event.equals("left-click")) setChecked(!checked); } public boolean isChecked() { diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 66b4136c..6547f77a 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -150,6 +150,8 @@ public void setPadding(V4Int padding) { @Override public void handleBuiltinEvent(String event, Object... args) { + super.handleBuiltinEvent(event, args); + switch (event) { case "left-click" -> { if (isFocused()) { @@ -209,8 +211,6 @@ public void handleBuiltinEvent(String event, Object... args) { case "hover-leave" -> hoveredItem = null; } - - super.handleBuiltinEvent(event, args); } private int getItemIndexAt(int mouseY) { diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 16d44c62..f6fdf579 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -95,14 +95,14 @@ public void render() { @Override public void handleBuiltinEvent(String event, Object... args) { + super.handleBuiltinEvent(event, args); + if (event.equals("left-click")) { textSelection.clear(); if (Vera.getMouseX() < x) cursorPos = 0; else if (Vera.getMouseX() > x + Vera.provider.getTextWidth(text, font)) cursorPos = text.length(); } - - super.handleBuiltinEvent(event, args); } public VFont getFont() { diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index f68a921c..dd5fc1ac 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -58,6 +58,8 @@ public void render() { @Override public void handleBuiltinEvent(String event, Object... args) { + super.handleBuiltinEvent(event, args); + switch (event) { case "mouse-move" -> getHoveredTabIndex((int) args[0]); @@ -92,8 +94,6 @@ public void handleBuiltinEvent(String event, Object... args) { events.fire("vtabwidget-tab-right-click-release", hoveredTab); } } - - super.handleBuiltinEvent(event, args); } public boolean isValidTabIndex(@Nullable Integer index) { diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 7acb78e5..51af71e6 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -27,8 +27,6 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { this.rotation = 0; - addVisibilityCondition(() -> visible); - setStyle("overlay", VColor.transparent()); setStyle("cursor", VCursorShape.DEFAULT); @@ -37,9 +35,6 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { setStyle("border-size", new V4Int(0)); } - @Override - public abstract void render(); - public int getHitboxX() { return getX(); } From 0f48e0aa03ac377a36c4e5f8c89f9921175e12ed Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 15:33:02 +0200 Subject: [PATCH 156/661] fix sendMessage not sending message --- src/main/java/net/snackbag/vera/VElement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 1c70c0ae..6717783f 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -67,7 +67,7 @@ public void onMessage(VWidgetMessageEvent executor) { } public void sendMessage(VElement element, String type, @Nullable Object content) { - events.fire("elem-message", new VWidgetMessageEvent.Context(this, type, content)); + element.events.fire("elem-message", new VWidgetMessageEvent.Context(this, type, content)); } // From 3f7571c97b8489567ea37b501981b47f1389820b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 17:13:42 +0200 Subject: [PATCH 157/661] move render method from VElement -> VWidget --- src/main/java/net/snackbag/vera/VElement.java | 1 - src/main/java/net/snackbag/vera/widget/VWidget.java | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 6717783f..e1e677f7 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -35,7 +35,6 @@ public VElement(VeraApp app, int x, int y, int width, int height) { this.height = height; } - public abstract void render(); public void handleBuiltinEvent(String name, Object... args) {} // diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 51af71e6..20b4cd38 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -35,6 +35,8 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { setStyle("border-size", new V4Int(0)); } + public abstract void render(); + public int getHitboxX() { return getX(); } From c1cbb2ec819d566d9115bdd8d72cecbe023ee546 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 17:14:23 +0200 Subject: [PATCH 158/661] update to super --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 9 +++++++++ src/main/java/net/snackbag/vera/layout/VVLayout.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index a3455e84..d5610576 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -1,4 +1,13 @@ package net.snackbag.vera.layout; +import net.snackbag.vera.core.VeraApp; + public class VHLayout extends VLayout { + public VHLayout(VeraApp app, int x, int y, int width, int height) { + super(app, x, y, width, height); + } + + public VHLayout(VeraApp app, int x, int y) { + this(app, x, y, -1, -1); + } } diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 92f5642a..38eb6a0d 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -1,4 +1,13 @@ package net.snackbag.vera.layout; +import net.snackbag.vera.core.VeraApp; + public class VVLayout extends VLayout { + public VVLayout(VeraApp app, int x, int y, int width, int height) { + super(app, x, y, width, height); + } + + public VVLayout(VeraApp app, int x, int y) { + this(app, x, y, -1, -1); + } } From 20ed8a6ae05707c3586fea94288bd53309d0a27d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 17:15:32 +0200 Subject: [PATCH 159/661] base VLayout --- .../net/snackbag/vera/layout/VLayout.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index c73e88cc..c1db505b 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -1,4 +1,31 @@ package net.snackbag.vera.layout; -public abstract class VLayout { +import net.snackbag.vera.VElement; +import net.snackbag.vera.core.VeraApp; + +import java.util.ArrayList; +import java.util.List; + +public abstract class VLayout extends VElement { + private final List elements = new ArrayList<>(); + + public VLayout(VeraApp app, int x, int y, int width, int height) { + super(app, x, y, width, height); + } + +// @Override +// public int getWidth() { +// return width < 0 ? calculateElementsWidth() : Math.min(calculateElementsWidth(), width); +// } + + @Override + public int getHeight() { + return height < 0 ? calculateElementsHeight() : Math.min(calculateElementsHeight(), height); + } + + public int calculateElementsHeight() { + return elements.parallelStream() + .mapToInt(VElement::getHeight) + .sum(); + } } From a11ea99b5d5cc8845f535baa45d3b48740c9a92e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 17:17:00 +0200 Subject: [PATCH 160/661] stop duplicate widgets --- src/main/java/net/snackbag/vera/core/VeraApp.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 08682ab0..825f87c6 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -194,6 +194,7 @@ public List> getWidgetsReversed() { } public void addWidget(VWidget widget) { + if (widgets.contains(widget)) return; this.widgets.add(widget); } From 6b51045897f090b0520a26604e7895211c5e761f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 17:30:35 +0200 Subject: [PATCH 161/661] addElement method --- src/main/java/net/snackbag/vera/layout/VLayout.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index c1db505b..d9641fe0 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -28,4 +28,9 @@ public int calculateElementsHeight() { .mapToInt(VElement::getHeight) .sum(); } + + public void addElement(VElement elem) { + if (elements.contains(elem)) return; + elements.add(elem); + } } From 8d3ba0896961962108479db26efb0145db71911f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:02:19 +0200 Subject: [PATCH 162/661] add render cache id --- src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java | 5 +++++ src/main/java/net/snackbag/vera/Vera.java | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java index 754a63df..748fae32 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java @@ -11,6 +11,11 @@ @Mixin(InGameHud.class) public abstract class InGameHudMixin { + @Inject(at = @At(value = "HEAD"), method = "render") + private void mcvera$beginRender(DrawContext context, float tickDelta, CallbackInfo ci) { + Vera.renderCacheId = System.currentTimeMillis(); + } + @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;enableBlend()V", shift = At.Shift.AFTER, ordinal = 0, remap = false), method = "render") private void mcvera$renderBelowVignette(DrawContext context, float tickDelta, CallbackInfo ci) { Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_VIGNETTE); diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index 7f31b7fc..bf214f11 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -24,6 +24,8 @@ public class Vera { public static final String FONT_DEFAULT = provider.getDefaultFontName(); public static final String FONT_ARIAL = "minecraft:arial"; + public static long renderCacheId = 0; + public static void forVisibleAndAllowedApps(Consumer handler) { final List handledApps = new ArrayList<>(); if (!MCVeraData.appHierarchy.isEmpty()) { From 8abaaa3342da0c31d6baaedfb760dda0e60192b3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:02:33 +0200 Subject: [PATCH 163/661] implement cache and posOf method --- .../net/snackbag/vera/layout/VLayout.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index d9641fe0..4ba173ee 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -1,14 +1,25 @@ package net.snackbag.vera.layout; import net.snackbag.vera.VElement; +import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; +import org.joml.Vector2i; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; public abstract class VLayout extends VElement { private final List elements = new ArrayList<>(); + /** + * ID for the last position cache call. To be compared with the current render cache ID + * + * @reason So we don't calculate the positions of widgets for each getX or getY call + */ + private long cacheId = 0; + private final HashMap cache = new HashMap<>(); + public VLayout(VeraApp app, int x, int y, int width, int height) { super(app, x, y, width, height); } @@ -23,6 +34,15 @@ public int getHeight() { return height < 0 ? calculateElementsHeight() : Math.min(calculateElementsHeight(), height); } + public abstract Vector2i posOf(VElement elem); + + private void checkCache() { + if (cacheId != Vera.renderCacheId) { + cache.clear(); + cacheId = Vera.renderCacheId; + } + } + public int calculateElementsHeight() { return elements.parallelStream() .mapToInt(VElement::getHeight) From 9aa9aecded851e2cccb775689c75c37f1f77b451 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:02:42 +0200 Subject: [PATCH 164/661] take layout into account --- src/main/java/net/snackbag/vera/VElement.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index e1e677f7..518ee57a 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -3,6 +3,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.EventHandler; import net.snackbag.vera.event.VWidgetMessageEvent; +import net.snackbag.vera.layout.VLayout; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -21,6 +22,8 @@ public abstract class VElement { public final VeraApp app; private final List> visibilityConditions = new ArrayList<>(); + protected @Nullable VLayout layout; + public VElement(VeraApp app, int x, int y, int width, int height) { this.app = app; @@ -74,11 +77,11 @@ public void sendMessage(VElement element, String type, @Nullable Object content) // public int getX() { - return x; + return layout != null ? layout.posOf(this).x : x; } public int getY() { - return y; + return layout != null ? layout.posOf(this).y : y; } public void move(int both) { @@ -114,4 +117,6 @@ public void setSize(int width, int height) { this.width = width; this.height = height; } + + public } From 92e0547a02f2fd54b54285bb45c23f189c78603c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:08:25 +0200 Subject: [PATCH 165/661] elements variable private -> protected --- src/main/java/net/snackbag/vera/layout/VLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 4ba173ee..95c0f787 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -10,7 +10,7 @@ import java.util.List; public abstract class VLayout extends VElement { - private final List elements = new ArrayList<>(); + protected final List elements = new ArrayList<>(); /** * ID for the last position cache call. To be compared with the current render cache ID From c949d18412db495847fe2fe89490191c64bd3af4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:08:29 +0200 Subject: [PATCH 166/661] cache variable private -> protected --- src/main/java/net/snackbag/vera/layout/VLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 95c0f787..3fb46d79 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -18,7 +18,7 @@ public abstract class VLayout extends VElement { * @reason So we don't calculate the positions of widgets for each getX or getY call */ private long cacheId = 0; - private final HashMap cache = new HashMap<>(); + protected final HashMap cache = new HashMap<>(); public VLayout(VeraApp app, int x, int y, int width, int height) { super(app, x, y, width, height); From 8d8aaa033a8002ba806adecb70b646e736fa5593 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:08:38 +0200 Subject: [PATCH 167/661] un-abstract posOf method --- src/main/java/net/snackbag/vera/layout/VLayout.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 3fb46d79..ed6169a6 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -34,7 +34,12 @@ public int getHeight() { return height < 0 ? calculateElementsHeight() : Math.min(calculateElementsHeight(), height); } - public abstract Vector2i posOf(VElement elem); + public Vector2i posOf(VElement elem) { + checkCache(); + + if (!cache.containsKey(elem)) throw new RuntimeException("Layout cache does not contain requested element"); + return cache.get(elem); + } private void checkCache() { if (cacheId != Vera.renderCacheId) { From 3f6f9e9c525934e94cb4cb4e6c3c43c1fadfdb1a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:08:48 +0200 Subject: [PATCH 168/661] make rebuild abstract instead --- src/main/java/net/snackbag/vera/layout/VLayout.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index ed6169a6..ebe19ea4 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -45,9 +45,13 @@ private void checkCache() { if (cacheId != Vera.renderCacheId) { cache.clear(); cacheId = Vera.renderCacheId; + + rebuild(); } } + public abstract void rebuild(); + public int calculateElementsHeight() { return elements.parallelStream() .mapToInt(VElement::getHeight) From a2e53a29b6d3b5f8a0e2341189779a57f664f217 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:08:57 +0200 Subject: [PATCH 169/661] implement vertical layout --- .../java/net/snackbag/vera/layout/VVLayout.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 38eb6a0d..fba68b9a 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -1,6 +1,8 @@ package net.snackbag.vera.layout; +import net.snackbag.vera.VElement; import net.snackbag.vera.core.VeraApp; +import org.joml.Vector2i; public class VVLayout extends VLayout { public VVLayout(VeraApp app, int x, int y, int width, int height) { @@ -10,4 +12,15 @@ public VVLayout(VeraApp app, int x, int y, int width, int height) { public VVLayout(VeraApp app, int x, int y) { this(app, x, y, -1, -1); } + + @Override + public void rebuild() { + int y = 0; + + for (VElement elem : elements) { + cache.put(elem, new Vector2i(x, y)); + + y += elem.getHeight(); + } + } } From f37392989e5ae4323a9b0494eafc3b4207ce623b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:09:03 +0200 Subject: [PATCH 170/661] implement horizontal layout --- .../java/net/snackbag/vera/layout/VHLayout.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index d5610576..37e3b9f5 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -1,6 +1,8 @@ package net.snackbag.vera.layout; +import net.snackbag.vera.VElement; import net.snackbag.vera.core.VeraApp; +import org.joml.Vector2i; public class VHLayout extends VLayout { public VHLayout(VeraApp app, int x, int y, int width, int height) { @@ -10,4 +12,15 @@ public VHLayout(VeraApp app, int x, int y, int width, int height) { public VHLayout(VeraApp app, int x, int y) { this(app, x, y, -1, -1); } + + @Override + public void rebuild() { + int x = 0; + + for (VElement elem : elements) { + cache.put(elem, new Vector2i(x, y)); + + x += elem.getWidth(); + } + } } From 15264fb4d430664d643e813744e9dc6218ec6d16 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:52:31 +0200 Subject: [PATCH 171/661] adjust size in constructor --- src/main/java/net/snackbag/vera/widget/VLabel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index ec0c4451..5931d4d0 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -20,6 +20,8 @@ public VLabel(String text, VeraApp app) { setStyle("background-color", VColor.transparent()); setStyle("font", VFont.create()); + + adjustSize(); } public String getText() { From 3ef4945e915f711ab5cc6244e253b732e9221b13 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 18:59:07 +0200 Subject: [PATCH 172/661] very cursed testing setup --- .../mcvera/test/LayoutTestApplication.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java index c4027ddb..6b59dfab 100644 --- a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java @@ -2,6 +2,9 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.layout.VHLayout; +import net.snackbag.vera.layout.VLayout; +import net.snackbag.vera.layout.VVLayout; import net.snackbag.vera.widget.VLabel; public class LayoutTestApplication extends VeraApp { @@ -11,13 +14,19 @@ public class LayoutTestApplication extends VeraApp { public void init() { new VShortcut(this, "escape", this::hide).alsoAdd(); - /* - VLayout layout = new VVLayout(this); - VLabel label = new VLabel("I'm a test", this).alsoAddTo(layout); + VLayout layout = new VVLayout(this, 0, 0); + VLabel label = new VLabel("I'm a test", this).alsoAdd(); layout.addElement(label); + VLabel label2 = new VLabel("I'm another test", this).alsoAdd(); layout.addElement(label2); - VLayout secondLayout = new VHLayout(this); - VLabel horiz1 = new VLabel("1", this).alsoAddTo(secondLayout); - VLabel horiz2 = new VLabel("2", this).alsoAddTo(secondLayout); - */ + VLayout secondLayout = new VHLayout(this, 0, 0); + VLabel horiz1 = new VLabel("1", this).alsoAdd(); secondLayout.addElement(horiz1); + VLabel horiz2 = new VLabel("2", this).alsoAdd(); secondLayout.addElement(horiz2); + + VLayout thirdLayout = new VVLayout(this, 0, 0); + VLabel label3 = new VLabel("oh?", this).alsoAdd(); thirdLayout.addElement(label3); + VLabel label4 = new VLabel("oh!!!!", this).alsoAdd(); thirdLayout.addElement(label4); + + secondLayout.addElement(thirdLayout); + layout.addElement(secondLayout); } } From 98e3323402bafbb54d598169ffeee8cbad49b13b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:08:22 +0200 Subject: [PATCH 173/661] update to be compatible with layouts --- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 2 +- src/main/java/net/snackbag/vera/widget/VDropdown.java | 10 ++++++++-- src/main/java/net/snackbag/vera/widget/VImage.java | 2 +- src/main/java/net/snackbag/vera/widget/VLabel.java | 7 +++++-- src/main/java/net/snackbag/vera/widget/VLineInput.java | 9 +++++++-- src/main/java/net/snackbag/vera/widget/VRect.java | 2 +- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 3 +++ src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 8 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 263e6deb..7e602648 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -41,7 +41,7 @@ public void render() { StyleState state = createStyleState(); Identifier texture = checked ? getStyle("src-checked", state) : getStyle("src", state); - Vera.renderer.drawImage(app, x, y, width, height, 0, texture); + Vera.renderer.drawImage(app, getX(), getY(), width, height, 0, texture); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 6547f77a..4d7acae0 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -40,6 +40,9 @@ public void render() { StyleState state = createStyleState(); VColor backgroundColor = getStyle("background-color", state); + int x = getX(); + int y = getY(); + Vera.renderer.drawRect( app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), 0, backgroundColor @@ -118,12 +121,12 @@ public VColor.ColorModifier modifyItemHoverColor() { @Override public int getHitboxX() { - return x - padding.get3(); + return getX() - padding.get3(); } @Override public int getHitboxY() { - return y - padding.get1(); + return getY() - padding.get1(); } @Override @@ -152,6 +155,9 @@ public void setPadding(V4Int padding) { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); + int x = getX(); + int y = getY(); + switch (event) { case "left-click" -> { if (isFocused()) { diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index f044803d..0be47e49 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -22,6 +22,6 @@ public void render() { StyleState state = createStyleState(); Identifier src = getStyle("src", state); - Vera.renderer.drawImage(app, x, y, width, height, rotation, src); + Vera.renderer.drawImage(app, getX(), getY(), width, height, rotation, src); } } diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 5931d4d0..bc1996a6 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -54,12 +54,12 @@ public int getHitboxHeight() { @Override public int getHitboxX() { - return x - padding.get4(); + return getX() - padding.get4(); } @Override public int getHitboxY() { - return y - padding.get1(); + return getY() - padding.get1(); } public VAlignmentFlag getAlignment() { @@ -96,6 +96,9 @@ public void render() { VFont font = getStyle("font", state); VColor backgroundColor = getStyle("background-color", state); + int x = getX(); + int y = getY(); + Vera.renderer.drawRect( app, getHitboxX(), diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index f6fdf579..6551e413 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -48,6 +48,9 @@ public void render() { VColor backgroundColor = getStyle("background-color", state); VColor textSelectionColor = getStyle("select-color", state); + int x = getX(); + int y = getY(); + Vera.renderer.drawRect( app, getHitboxX() + app.getX(), @@ -97,6 +100,8 @@ public void render() { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); + int x = getX(); + if (event.equals("left-click")) { textSelection.clear(); @@ -440,12 +445,12 @@ public int getHitboxHeight() { @Override public int getHitboxX() { - return x - padding.get4(); + return getX() - padding.get4(); } @Override public int getHitboxY() { - return y - padding.get1(); + return getY() - padding.get1(); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 7a793b5c..cdf98afe 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -17,6 +17,6 @@ public VRect(VColor color, VeraApp app) { public void render() { StyleState state = createStyleState(); - Vera.renderer.drawRect(app, x, y, width, height, rotation, getStyle("color", state)); + Vera.renderer.drawRect(app, getX(), getY(), width, height, rotation, getStyle("color", state)); } } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index dd5fc1ac..593a7d64 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -37,6 +37,9 @@ public void render() { int marginX = 0; int i = -1; + int x = getX(); + int y = getY(); + for (String key : tabs.keySet()) { int textWidth = Vera.provider.getTextWidth(key, font); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 20b4cd38..abddf64b 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -128,7 +128,7 @@ public void renderBorder() { public void renderOverlay() { StyleState state = createStyleState(); - Vera.renderer.drawRect(app, x, y, width, height, 0, getStyle("overlay", state)); + Vera.renderer.drawRect(app, getX(), getY(), width, height, 0, getStyle("overlay", state)); } public boolean isLeftClickDown() { From eec73c54b49bd1c8317eff8cbde9bf35299e8fe5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:08:38 +0200 Subject: [PATCH 174/661] fix up positioning and support nested layouts --- src/main/java/net/snackbag/vera/layout/VVLayout.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index fba68b9a..162d202b 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -15,10 +15,10 @@ public VVLayout(VeraApp app, int x, int y) { @Override public void rebuild() { - int y = 0; + int y = getY(); for (VElement elem : elements) { - cache.put(elem, new Vector2i(x, y)); + cache.put(elem, new Vector2i(getX(), y)); y += elem.getHeight(); } From e2f10fc9ed182434b8cb5888a435731cdc7f7fb5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:09:10 +0200 Subject: [PATCH 175/661] fix up positioning and support nested layouts --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index 37e3b9f5..f9cb7cf0 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -15,10 +15,10 @@ public VHLayout(VeraApp app, int x, int y) { @Override public void rebuild() { - int x = 0; + int x = getX(); for (VElement elem : elements) { - cache.put(elem, new Vector2i(x, y)); + cache.put(elem, new Vector2i(x, getY())); x += elem.getWidth(); } From 86f33b8fdd8eddf9c6ffc444b6858b4080a9607c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:09:22 +0200 Subject: [PATCH 176/661] fix crashes but no more parallel checking --- src/main/java/net/snackbag/vera/core/VeraApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 825f87c6..28158cbe 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -247,7 +247,7 @@ public List getShortcuts() { int mx = px - x; int my = py - y; - return getWidgetsReversed().parallelStream() + return getWidgetsReversed().stream() .filter(widget -> isPointOverWidget(widget, mx, my)) .filter(VWidget::visibilityConditionsPassed) .findFirst().orElse(null); From 6c0409dbaa56b3b99bb1fde5785dc462362fba2b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:09:32 +0200 Subject: [PATCH 177/661] make x and y private --- src/main/java/net/snackbag/vera/VElement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 518ee57a..dcc6d46f 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -11,8 +11,8 @@ import java.util.function.Supplier; public abstract class VElement { - protected int x; - protected int y; + protected int _x; + protected int _y; protected int width; protected int height; From e8e352239d71fa951bf4b5aa1a8d7cc872898222 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:09:47 +0200 Subject: [PATCH 178/661] consequences of making x and y private --- src/main/java/net/snackbag/vera/VElement.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index dcc6d46f..b2356daa 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -32,8 +32,8 @@ public VElement(VeraApp app, int x, int y, int width, int height) { addVisibilityCondition(() -> visible); - this.x = x; - this.y = y; + this._x = x; + this._y = y; this.width = width; this.height = height; } @@ -77,11 +77,11 @@ public void sendMessage(VElement element, String type, @Nullable Object content) // public int getX() { - return layout != null ? layout.posOf(this).x : x; + return layout != null ? layout.posOf(this).x : _x; } public int getY() { - return layout != null ? layout.posOf(this).y : y; + return layout != null ? layout.posOf(this).y : _y; } public void move(int both) { @@ -89,8 +89,8 @@ public void move(int both) { } public void move(int x, int y) { - this.x = x; - this.y = y; + this._x = x; + this._y = y; } public int getWidth() { From cb9174065194331f49c53218493c2c1d7e4c224d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:10:06 +0200 Subject: [PATCH 179/661] more clarification when throwing errors --- src/main/java/net/snackbag/vera/layout/VLayout.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index ebe19ea4..4912d585 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -37,7 +37,7 @@ public int getHeight() { public Vector2i posOf(VElement elem) { checkCache(); - if (!cache.containsKey(elem)) throw new RuntimeException("Layout cache does not contain requested element"); + if (!cache.containsKey(elem)) throw new RuntimeException("Layout cache does not contain requested element '%s'".formatted(elem.toString())); return cache.get(elem); } @@ -61,5 +61,6 @@ public int calculateElementsHeight() { public void addElement(VElement elem) { if (elements.contains(elem)) return; elements.add(elem); + elem.layout = this; } } From 9aab8349811d0b83637df5f903e3d6fdc2ea536a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:17:22 +0200 Subject: [PATCH 180/661] apply layout variable --- src/main/java/net/snackbag/vera/VElement.java | 9 +++++++-- src/main/java/net/snackbag/vera/layout/VLayout.java | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index b2356daa..a8a505c6 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; public abstract class VElement { @@ -36,6 +37,8 @@ public VElement(VeraApp app, int x, int y, int width, int height) { this._y = y; this.width = width; this.height = height; + + onLayoutSwap(layout -> this.layout = layout); // event gets called. we use the event itself to change the layout } public void handleBuiltinEvent(String name, Object... args) {} @@ -72,6 +75,10 @@ public void sendMessage(VElement element, String type, @Nullable Object content) element.events.fire("elem-message", new VWidgetMessageEvent.Context(this, type, content)); } + public void onLayoutSwap(Consumer executor) { + events.register("elem-layout-swap", args -> executor.accept((VLayout) args[0])); + } + // // Position & Size // @@ -117,6 +124,4 @@ public void setSize(int width, int height) { this.width = width; this.height = height; } - - public } diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 4912d585..66b4cfaf 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -61,6 +61,6 @@ public int calculateElementsHeight() { public void addElement(VElement elem) { if (elements.contains(elem)) return; elements.add(elem); - elem.layout = this; + elem.events.fire("elem-layout-swap", this); } } From 9b8b3a30c105dd76f5760c6e465cb042ef860997 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:43:54 +0200 Subject: [PATCH 181/661] add QOL alsoAddTo method --- src/main/java/net/snackbag/vera/VElement.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index a8a505c6..bd6f35b6 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -124,4 +124,9 @@ public void setSize(int width, int height) { this.width = width; this.height = height; } + + public T alsoAddTo(VLayout layout) { + layout.addElement(this); + return (T) this; + } } From c114b490e1a990958fcc4dae3b4532dea9c4ee7d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:44:03 +0200 Subject: [PATCH 182/661] clean up for readability --- .../mcvera/test/LayoutTestApplication.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java index 6b59dfab..3d6adfda 100644 --- a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java @@ -15,18 +15,15 @@ public void init() { new VShortcut(this, "escape", this::hide).alsoAdd(); VLayout layout = new VVLayout(this, 0, 0); - VLabel label = new VLabel("I'm a test", this).alsoAdd(); layout.addElement(label); - VLabel label2 = new VLabel("I'm another test", this).alsoAdd(); layout.addElement(label2); + new VLabel("I'm a test", this).alsoAddTo(layout); + new VLabel("I'm another test", this).alsoAddTo(layout); - VLayout secondLayout = new VHLayout(this, 0, 0); - VLabel horiz1 = new VLabel("1", this).alsoAdd(); secondLayout.addElement(horiz1); - VLabel horiz2 = new VLabel("2", this).alsoAdd(); secondLayout.addElement(horiz2); + VLayout secondLayout = new VHLayout(this, 0, 0).alsoAddTo(layout); + new VLabel("1", this).alsoAddTo(secondLayout); + new VLabel("2", this).alsoAddTo(secondLayout); - VLayout thirdLayout = new VVLayout(this, 0, 0); - VLabel label3 = new VLabel("oh?", this).alsoAdd(); thirdLayout.addElement(label3); - VLabel label4 = new VLabel("oh!!!!", this).alsoAdd(); thirdLayout.addElement(label4); - - secondLayout.addElement(thirdLayout); - layout.addElement(secondLayout); + VLayout thirdLayout = new VVLayout(this, 0, 0).alsoAddTo(secondLayout); + new VLabel("oh?", this).alsoAddTo(thirdLayout); + new VLabel("oh!!!!", this).alsoAddTo(thirdLayout); } } From ad7ddcbc6a3bf9af72ba3c237a0771d00cb1f3a8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:50:49 +0200 Subject: [PATCH 183/661] clean up layouts --- .../java/net/snackbag/mcvera/test/LayoutTestApplication.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java index 3d6adfda..36ae1adb 100644 --- a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java @@ -18,11 +18,11 @@ public void init() { new VLabel("I'm a test", this).alsoAddTo(layout); new VLabel("I'm another test", this).alsoAddTo(layout); - VLayout secondLayout = new VHLayout(this, 0, 0).alsoAddTo(layout); + VLayout secondLayout = new VHLayout(layout); new VLabel("1", this).alsoAddTo(secondLayout); new VLabel("2", this).alsoAddTo(secondLayout); - VLayout thirdLayout = new VVLayout(this, 0, 0).alsoAddTo(secondLayout); + VLayout thirdLayout = new VVLayout(secondLayout); new VLabel("oh?", this).alsoAddTo(thirdLayout); new VLabel("oh!!!!", this).alsoAddTo(thirdLayout); } From 66680492d50213291e7bd8e1de3c51e33579b6f4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:51:02 +0200 Subject: [PATCH 184/661] add QOL constructors --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 9 +++++++++ src/main/java/net/snackbag/vera/layout/VVLayout.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index f9cb7cf0..183c81f0 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -13,6 +13,15 @@ public VHLayout(VeraApp app, int x, int y) { this(app, x, y, -1, -1); } + public VHLayout(VLayout parent, int width, int height) { + this(parent.app, 0, 0, width, height); + this.alsoAddTo(parent); + } + + public VHLayout(VLayout parent) { + this(parent, -1, -1); + } + @Override public void rebuild() { int x = getX(); diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 162d202b..4a75d5c1 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -13,6 +13,15 @@ public VVLayout(VeraApp app, int x, int y) { this(app, x, y, -1, -1); } + public VVLayout(VLayout parent, int width, int height) { + this(parent.app, 0, 0, width, height); + this.alsoAddTo(parent); + } + + public VVLayout(VLayout parent) { + this(parent, -1, -1); + } + @Override public void rebuild() { int y = getY(); From e7dc831e4590ea3014b60d150e409f211c42acd3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 19:51:13 +0200 Subject: [PATCH 185/661] also add widget to app when alsoAddTo is used --- src/main/java/net/snackbag/vera/widget/VWidget.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index abddf64b..4ec51511 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.*; +import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; import java.nio.file.Path; @@ -304,4 +305,12 @@ public T alsoAdd() { app.addWidget(this); return (T) this; } + + @Override + public T alsoAddTo(VLayout layout) { + super.alsoAddTo(layout); + alsoAdd(); + + return (T) this; + } } From 86730e849fa14fc82f70e515c8560f7a72b61d36 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 20:21:48 +0200 Subject: [PATCH 186/661] allow for element removal --- src/main/java/net/snackbag/vera/VElement.java | 5 +++++ src/main/java/net/snackbag/vera/layout/VLayout.java | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index bd6f35b6..4443f616 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -39,6 +39,7 @@ public VElement(VeraApp app, int x, int y, int width, int height) { this.height = height; onLayoutSwap(layout -> this.layout = layout); // event gets called. we use the event itself to change the layout + onLayoutRemove(() -> this.layout = null); } public void handleBuiltinEvent(String name, Object... args) {} @@ -79,6 +80,10 @@ public void onLayoutSwap(Consumer executor) { events.register("elem-layout-swap", args -> executor.accept((VLayout) args[0])); } + public void onLayoutRemove(Runnable executor) { + events.register("elem-layout-remove", args -> executor.run()); + } + // // Position & Size // diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 66b4cfaf..9952cf76 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -63,4 +63,16 @@ public void addElement(VElement elem) { elements.add(elem); elem.events.fire("elem-layout-swap", this); } + + public boolean removeElement(VElement elem) { + if (!elements.contains(elem)) return false; + + elem.events.fire("elem-layout-remove"); + return elements.remove(elem); + } + + public void clear() { + for (VElement elem : elements) elem.events.fire("elem-layout-remove"); + elements.clear(); + } } From f8c5bad812be2f58bebb3c2dafff9c2f72e2f5e8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 20:33:52 +0200 Subject: [PATCH 187/661] update icon --- src/main/resources/assets/mcvera/icon.png | Bin 4881 -> 160933 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/mcvera/icon.png b/src/main/resources/assets/mcvera/icon.png index 43e7c1fd9d762e5069d85d73601be1f0da79062c..a1dbc980a6917a7fa08d37b993c996059a36f1ba 100644 GIT binary patch literal 160933 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

k~`V<=M> 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 z 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

%OTr};R?MR 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%0At`$L-n=3e*>G0s~Cv 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

`mC{f6`n`UASoa4n=A^Zb!-3DvNHhFL~kT$Fcd7 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 Hu0mjf!({X+ literal 160933 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

k~`V<=M> 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 z zK5f~e!f^l4hU&qHLm$Wu1$$1hG>@@AqZe?7bLVMAcudc8aDT2{^JK0=;t<6OmH9$m z0mSaB{5|d^fA#FQ|JOr0VrikxAZHP*CionQCc(*KV_z|3YyvVCvFk3&Z(F|LAx0x0 zZjIA$tG)SI*8`wD+cd#x&b-*8)ItPs0h@fnqhT_au=s8;>%P03xG#^xn1#qp>F_g> z0+Mg^tkLm#o^~IT<$bwnZu&Gl7S`*MYmGcZE(W)Tp0A!JaT+){5~@6#T4(GaG^Sik z-Va9}{0b#!{oy4ux-0a&nfD4q{h&VQuIC_&3k8*)XZjP?cYg=}-ep}ORF6%^me?L7 zPlEBj*zrfrzb|F})}&H(Uu&p7qb6b6NDS|x zB>{6iP&3f^NwA}e|4fp9@ecn#RLUakxsik>ca=j|&tH{+mW6~KG(90jYM_5W+^F)Y z=N|zTWXfj+$qUHbC(w)s8D@~lKw;ovwlm-cEBhrs$PRRZ)z2>IpU@(Y#DJoOw!WKoppa& zl)=o{PL$dSn-oziox$=Rp)-Rrcl$KQlmvfrP!VRSrAvDA@2@4B)2t<3o($QHuoL)0 zoaP9kAD5gDzPTW_e|XtBxK@FQMZ5x&^Dfk4;s>!Irv`+AHoa;7Yc%DH9EoxJhML5= z81j5ehmun#^;$hQngpiwHRT1Rnjg{|7F$i#KKL63H+6$}i6J*fAk^QX4@VW)_krK` z(vDEui3dZ+SHOW}lcEisE^Pdw0EJZ%kR-}3?wqm4AjtqGdH&@KR=TkmU9uHnp(Prf zxb0iYog!Ku@3Vc4+D;ZmfBnKS6=kgds`F1?v+w+h&v%ySL`qF#qQ-IYl@Isp;Z;e{ zzE0j<`J?4+3ObFmA?K(_z#&4fg=R$r<;|W-aW}+r=r+kUKUAB4(LN}soniAQ>CsBf zSOT`hN-YwG?6wiShHCptcni+zKkYr*T@M-Dsd?xQh!S{@J^4q#hd2xoqX&cJk51U) zK=z>ROIp{9MdThl33fimaoUA~Q;y_$GbP0BbN&PFs~@VkfePZc^{i=IQUpm6n<`8` z%+EeR4SN}%(U7A+Jl|_(BGwcI!#Jkko-Y)r^P>bhQ;&c4q;4c(w(unTp~SijF3 zTg7DCQEknOd-iR~#{34_l9IS=PCf5`@q0%MPHEA5VQ26~?hfx{$|N~iSXvJ7wIlkA zH#{nmb!+vVgWVTtTv*tx&8xkHC;l;YR*)AxMv^t>4kcOjkduc(|$@xsLE@D##JTzZv)U==93C;$FC*m1A= z=i05eZS(r5l_kj)Fy`ZUifuFM7lxl1)qbe{4|s_o84i(6L8ro6**w)y8a$+4X8!I2 zc>oYY*H9%LGzGaqVi%B)1P7vEes~?CfQ-ScO*SSHU^*O2${RazNY+f)|ZF!2937_+56-Rf36_k|FRs#u?eyW3dolQ@_5AMr3FR) zM%{px9{MPBZFyHR6(u!uk}T1TI_Qd48p+FVwq_6p znjcSP#bf)-I1ZH{m)Z56PN9O=Gb}YF>5Ig}&o%2x>b|=4a5bsTH3_1-QN#9@gKf39 zS;$27fTobf^PAS7Zm`rrkw0}g#^vPKVxGfaH^`SG#oi80pGMba+24F#3j&COkn5@6 z$0+u+!!-(A{dseaaUF-RrTc8lgrl$h6gdhh&Qw4yd`Tf$5&8LQ%l5=?cr*;$~PI2s>6sXR4gJ; z(>G~#?t3AgsA(CqJ+WyGc;YW9h-z5t&mFl$yizSB*eMoYuD(Z=eE{h$9ZHpD03vcF z&CDU9EefF=dPF)EgnmLTaJ6X^y!$4bpTLv5E8>2N%_&n9DAc8L&Xf9S{G-W=W$$k} zZNCHaGuuN&u=YWd0!2ZAula&yLrMPQ1BHjR&zO@&j2pK}6$mPAc^X$V)_$nZgNHA} z637-jwAz(TYHkZ>@C>{K&ts=WAM0@}pNak)3Azw%`OYyGLrWXHaP*2dQk>oQFm&A{ zM!uu9Pgg)4-M%lo($vpJrhGl%4OhiIl4DT@{hZ`-VWYAvZq?ll>zrU~;x=2nk= zT3@PSZCp-JTcheGCF3xpAnr)F&=;kxAK3}8P)lsoI;NG3)BTqkaIIe~->?dW_Ys~ylz+0SZ64^%wZ*CDH!>gJR{&hD574VvsXul3$ z%}>5O{$X1&7as@?5OP-#opY~1zkwudUgl=ZjpN;qy&$#HY%qA(i8=#^YS~=@o&{iM zme$mY{OVQ2tqN+M#Wyg-3kK-m2HGs}UejhLMHw1-6_xP}J@yj+{N@P-k_NDXP*Zp6 zh>^6=midPyCzgE+vqM zWWMl|`i33khW7p!q$zZhWPSW-KJ&F_%Ep+q!NUvX2csGH4zd)6_(IqJT1)dpLz@F6 z!>{lUT=sq3?BF$$x>`;BewXj!Jlh<`_fxv9f_i8XPgrCZoA-wN(U;o!P_{wVz$Sc6 zkrreZGJkBh~{>K<9c2M+nbiuDt|Fjz`!Kn#g2i zJm7h&$O>%cGrSXg9a^RV2R;@7B8;yZM~|AGb!|1SSHG91GUnxZtsw*5fWnoMC^7yt zCV-_FGiMu9ku)ST3VdJcB{7y0YABS$-EySLyL>VnYY1yL`~_Hv)Ch#f2COdTJc&)( zt{g|4rv19byW(JYw<;QVF0agVk0H@~RpDZdYHtnam`6pIg!$5{YQ)&S5fS?KU9gZ} z#nWG(B>BI?^sJGbH`sqIWv={jQ7#`3N1}%O&v1$21Z$7dV?rE6iV7C@9@N>J3Yp?v zfLkaX;BT$M<3NhbY(jd!aZz%xP$I$J%64LnJQ;SLy)kEBY#5$}@XJ(b>e~0{!sW4G z3=au$gk+6NSmX9NNJJy)ak)Lwt8_x*^7tv^|Dbk}MZi{s*t}dC0e<Qj{mof6O_rU9(RO6NXf2c=ln zipN!I=8~5jhxeoMC}QDWoE!8l?4cPXvl=S)GMM2knn3aSm^DU#t)AULx;`2DJxW`F zE%ex7-NYfM;XZ;AA-S~66K(Tj-Z+{p^rr^>qbnTuo<;dBHTjq3Ow*s*kq8E}?DJ)5 z(pX@dcybf9KkfznY`nxK-|bLQ0dsWji+ng5Pz_(bvp1^yvq`uhb!{>tNJSmt{iCBo zthG4ib-!>hM_BLXjbymc4fEgh9@&2Q@OA)sxGS&UI=G4F4`yjY*)Fhc!=X?sAV|zy zQsOTtM0r^UIRvFG0Xd7Kh~)w9_eHagFCB&no*kZ|!FXX@R1#J=K;$vlXgdT3J@^Y; zLs{$z#iQDzRQ|!8yDQQFnw&&O4*BuQ9>lzEKH}j!hCRzH%^NH{tbj^WGxwXrEAa?w z$kw#vi5Ug*9-0EuLvcK54^4WQj(N=L_mj;`Urd(LhzUYX7)i=K!~Z=j&9Td%fUs}Q zpOUYd^hk-vzbrVUI%0@^L38+~l_a08XXutehVAnXB`1T(JIMM^Md51q9X?3*wy4n) z=oYk+om@i>4GiE6A1NTp0oo)a5a08BJT*>=WI6K>-5Te!4ZaVboZmeBBSy9TX48&R zr$XmarARHcb4;3YDwu+!JeFysrT>BM7ry;Nl@p~7@C(mH$(VG#fxz^lb=KqGB+Lnp z8ZKO|d^v7vS5&BYU)`X7e~)Mc(S2L<+j)d^>#OS9)nbYH%jtp(7mOEOOLPA*7_mBy zHIpHkhC7sAecSG@<|#NB8!|hb>x37FxXXd3`D70(7v@S9RzA)hG?J2#(Vkff-T~iT zB5V&qdkw+>!El1ueo&XU+Tv}De>kC7C;+os2B{yAo-c7-h(&e2|NQ~hL7_vBn(ax; zY>`h@DFTkRO(4kYo(1#pr81?}ZI~#XWp0-XB=i&L6+;u$6YR`5m$zUyoWFe1i#Oa#~jHGVx4ZullFZmc?XjbVocugv&T$VbS zU@&W(lf0UZdaUhOm_Y@36V1`3E6$;DpU&}H-!Yx0#$)@s*W{`>SMT@6UFUtSO76zS zw4tX&(Xwo`aeVEK>yFOoE6sAREVfLDO6%Iq*9VW^`mO8LPlvYxvkx z0;1LcApR2dfY^uIsIxEFJX)0Pi9avTziQVYR=YL(3v*~JrIeZ~D&rCg0dy%K^4Pz} z{^L|q%+N9nsv%18H&yJSJ_VW-DGpi9w3C}~a1VMwl}NE~sQ=^x_Y9J3IKl?ymeF>hJr{jIoEY%Qk~7*%c8nV+&bR zO7<<3JqcN6Y;Qye*~%J4cG6ItAf(KC0vb^axzJ<2JC;mFcjX0n^v7tK- z=#_wGMV8A*(z7t^X)VZ$!g;WpLqc*NRhIiPtWfXdP;MV7TqbH*1GL^>`k?eLlzF%x zx^*IweU`a&A+Ra{+cf+e4=NZ}Mi6HPwou=S{w~Lnn_Z_W@tg<5^xfry4lRvvcH4g_9dnc7NY+e! zg9+SgPp7V)-$|;8T*5y+DQG-xT*B-nR3Av(yZW;qTRCqve}Jp95Y;U zvU|)~;NLz?MYKkOs5Uy6zDo-7+s)D9h(Up<$eCtT5{mJbN+`vVF{%piZXNNQoT}Z)m2ED5a$(j0hQeAvds&iP41RH4?YXG$I zalv#YF5<#g(z0pPyH+TZeftGiy53#qp7mOWL=^-Gk4P!oQUHOD7bMosEv>L)N&b+5 zDm~_#v1mlC69qX+dOf=jDk~|pGbE8WKx_IC2sPpGd0HLD^4rnz!?u@J4}u3z6*NN4 z>sPkVfq-XEavL>@xMkj#hfMae23*>3v3~BIy;XDD zX)Q8ko3Ix2q6Ph)VGK=W(OL>4b7JC<6 zfQDbG4_0*_!(suYbm?WD^<)*{t8=m+zbsa1%2<3Zi~~@bx{D&NeRJGvw84KHH9$lL z$gD_7ut35l6?-N*s%oUKRQ;B_q>=5;edsEk^#a7H+ru)hey0WN8bF?Nb5JYAi^C>#E|6&x^=SbN(!*FencCFm* zs@)2O-rC}eAQvGB2dh-1MapGl$^MYD_SZT5=x;L+bjzn*51biG(6Ds=C;=L^Bb z%@|&Fi-z4dKdR1^f0_3xcpd0sL75TWHj%%sJRa3j_T+h|lDxAZ;d#x>oSp-@lyo6+fvx?|ob6Us#e{%O!T$6PPNiUJlR z(vBJSPzY<_HMH;&_Sr*Pn9AN0Bj<+|QBSIm8>eo)fZfp*P?oZ&0*Hh-In7vDPgLr} z098q!(6Savb+Vm_b&=;o{P&{%YuOx05V27qIt+2~N=eG{fac|G>TQi5xr|P{o*75` zO#pKG0vxqcwtLPk>~^L>4J_4aW}Cqm9);&i561P;nvA}(Wjeg!WXK|WH?fTQ(>jQm zH?7Q_&iVpibj03peA%pkvm$(nQLiD9=LhfWz1$>$hjzJ^_1-_rVHVcX&bxtOSOzBp zgjHhN$U#HnFu;w;iS-3hwzLxZ`@|*_O$)llvVq9?iflsgBKTm0Z(fltmBLn(dq=nU zNkvP6xGMED%9EafI*Ya|WAKt|Z%L`Ft|T&beQ96h4W+OSN4+V4Ydw8N+V)F;0bmDE zzl9zJ{i1PnU)Sg^+prwqKvbsznT8Thc%qSVwOeIAej?QYbDECM0=U3Pi$#FUf| zw9yvgBN}wiU%?t#9kzw~X!+^u-6sd*OJ90Pot#BbVxRQ=>W=<7Gj6;gZF3O6tXo~0 z|5g21Rq^sC-x)mpD9Tbht*ur5-8GJnr90uw-EP}WoH4pf^poCt_Ta*zVX3 z`9(!Ad8$-FpWXYEV_7n3XDfP3q{vyjP0r28R!_bTF?x0_OZKUE{mzW1U2pG{laPzjWRkxVWJlg#a&{SH#&cg8A-IoFK(3vznvgEyNJA&lWLy>c~ z!-3zD7xl#pL^$>!(Cc7ygIvQ9=V=y6%@?03j4WD$mc^=B zUAjO=1y^~64|8l4ra0=vo7}Rq@&+24t*Uv~!|QD@vBRHf2c4Rn?uXn#@fz1_=~mvv zg|KX7P%DWru*|{v2&6S&itBb1Ugu#2(#QXULijfOtmmJQe5-9&yt*yd)arK^!$%hZ zU5#=FYTDoZGF{Xi+{{b34Tv~30Ga$OS#YOue_r8N|H{aupFOM8u%KT#Z zq;m$C58bm$`)#U0MTZ5r>!Jr!jj7W53=OeZ=-=4iKN};J4=a?v!Am3%ag!wy$jcWn zu0bqgl#d4dh5?w>wgqgDL+A2Iquq-p#4zRWoo6x@?qccG7Y-H2_~K8N}X_8sH> zI&nX=zaqGJtJ@a}O6PSRs zzprU%AANi`Euo7&;_H84b^wABXB{-MEE!E0E&j08SyoGXBFD>bP?r_#(MBe%nUIns z`je8Fe|NM^=I7Td8pV?@=UYBL9_Wf9vB{rV<6MY+EuKd+25Io){8`klV;t%)I^+X9 z6oZ?q=inSfS`r3cjZv>S(7K%u^BZgbOt@(Wd=v;{2Mx2J%2XLa!d*uDrj(-FkXZm< z;C)QU@vh=||68|v`s3*_;pkoD_-7%Q&Ewrdx=XiHjK8vA8h+VC2?Wr}cKFB~YPHig z`!gHfZ%tA0zP3NyaKu~6y z8}p(m+31TH(eS2yD!G2YK7sFft9+e<^IKEW&r6Ia$#h2HUEfToH~K*!D0X1br=h0+ zo}atlQS=_E^A@J@z&gd#se#Qt^Nh^h!6Mx%lrtIHIqLe01y=T)*WdN-BEOUKdGBWL zRCh94I?_Iq8bxZUIy&(_W)qMIqZ~2|@gA_6#+$Ky$H`El2zL&GO_3vn7#rZZR;Sa%jrsi3e+gPgVLA-}Ssl_~mpM7$ zoDJ=7{J3rrk(q>eINwl8M$HJ<;X7l2I`zE1}q+A{QCpM^@3I?=y7 zYge|lR@y7;Zgc%c*>{b$3B~QsdDs4#hpEfoPA?L*2zjO zJ>r)oJG3M3` zLx=c6k>z)28K8*#uKFA|Cs6;%0l(W+(Tq@T!@G3@)bHl$iVz~1dD5MFo>b<89}3$7 z^}-ZXJRw8+3MSIOZzz-IvxeO|y=^=fWJMZBL1#t#lkO@~RmLJeXtv+%*6Um3gD2$f zb_j!5L*dM_F8TCG?Js4ZiHv`l~acUHV&cfc|?fqZrH4&b7=ID<6$iL z9LS%(Hh8ZJIF!eYkI9M$#M-h-DB5rIY%;&}{Ka;b1!|n>)(MPZ@5!iK2maz|B`xABTIs;i=cwb; z94z7RW|cAjFy)YZw4D0)O|7nC8$4X!51G&4b@J;Z)k^lShA(dCy!@+WYM?!5W&h-5 zViLujeo8KA#m%$~__WZCgo+OO)>BZd$4oN3-rP?k_nCFP5y3@)lTu(zU}}A2zKWCc zdMt-P9jlaZtd76e1VzU4K5XdMu)z*K!eJtAVOh;{K6e?*Vu~@~JLvXj%KcR1z?7 zz>k5zWT6EAh$TW!c5>*EQx8R!x>N6iqaQ$b^LCN(Nmh zwNto)msa|-6nQ&}u{32EP^#_SaWgiyS=;=f#J=^urn1g~Wlf6?x%)SsJZ&H2PgeiD z5G~kmRZjHj5U&On^`?d@;$4xkov53dO~;#Y@G_rC_U~cWyju{Dq3VZ5ad3)_5exO6 z3Ke#noL<4MuqP$!^R{#1#Jm8J9HJxcMS)RSY*SOFET29_>>u9vY4(X~DnWhYiLdlc z%%DH{;@U38O|@v|UrtS@LbnNdKQyR+zacLw2>cH__G3(=JjB=hA$zEkxnj@` zYR)u9*t*S$GTl(>N0KJ*!e!nHa3R#S!6v z8N!zr_nw@aPf|QziRd+jDT#=RJgvWg7`Hd1I04U$cv9bLjv z(d&=V{BX?DNNrzjN=)9q=0wf5s``zy&7iKW+Q$|49-uhg#*^o*yl8;(Q+UMc1gP1D z>qd&l9aBfrK9C=Iop7dIz^rF^Gt+*;hak_hh#t_`_$H+lnM|+6%?zo3{LYHAaTu6j z=A9dbgZ3&QW9U9(3UY|W`|pZUHFY`zs#M5-?QM;5G2{%w+N;dhDVxUPR6jKNUcovc z&y#9UyGfp>oc*xgTTNOIBMpvGb~Ypp$R3?9{%K8(4Qzx%cvpM@vTy<-j5a|4HCLzV zmlCL~zb9kNE<5ex|E54bh)UsP@@N)TeYXjy$BQyt0!9}|OL4X{U&C)c_o_)dhL_JTq>w^@7uF8t#+^ec=8sDg0Z-&qd}gi zWZ|y~_Y>c=QBYIty_1ChF!F}a3hlJ!q_AjX)rkN^+ zoMnFPaPho&oU%y&p3{WH_1cnp2J|@2r02_ z2k|if=Qj_32Q(t!zn(0tDmZTDo~&aU3?`bZ{^RPVS3WtV7RaBy!6|^i07+zSZVshu z6x;L&8IT9ecy(UKo-0POSz7>x5l@-Hv*M6e*n$qG)%DUry%`2q53$%kt92u--wI>C zL8jS7>e2#}b@@=Rg$aOy*jpyhRMwTx+ihDh0RQ>85{DiAD>B*-^<$m5_kwuoP!mnz zLlTf`=hr4gZ!2nuWVh_ z87(udtwyaazyI3;uN;Ex))ZSq)}Lwz(<=I}oj)4SzJa+!o{NP)o#5Q=ZfK{Gg&;~d zOc<cJf>Hh`PK#&pu z@%!?~fCLCbKX!;XZ<6?hoT@Ve507WQ{91}1=9XnhfMBK^jgs!2^JohPcZm!zHUH4! zov06;e$LT=tT#s&nZY6$@j4{N4I7Qo+Kw6gOpC2ny+wJ+2w=G6J#4jbvhX>|mO+AT z#)rwX-JcCbLL8O=#X5G<;~2dW`VOC8Ky-pHKA3S2279elKiVu0OU3kN(K51g=nXDh zpEHc?ep&W5_3*7#OM;r&k2~jYJKas7NqZyGVa63LZ%RlLk_f$YXn14-UtnB7|+XltLXh_%gjEsx0tvw*vJML)<+@*2leC+o*1OleKibd6vB+eJrPC z-vB1x0#B!X_C?Oy>4RnldgM`yJNB0f&p8NArev=zs4tw1wWpzi@033h zjAjEVJ=lSKHek_OR~sZg`0o#I|1OH^&TRbW-QR-car;=lY$;&GH4ZpGt4JN^^6oh4 zo1_Wl+)I{bWP%C~%frhWiL&t?ix z+&UWuc~HssK^;{9PF2hR$u-Qpk~Y$L0(COQu@@I)`{iu1DI_qV@mkadI4per6F;7(BY|PNTvfB7{nvLfld&Zu&Jl^}N1bgOK zDA;jt8>yxMrkTgq>N~LIss4#DVshjdjJF}fh@yhWZ?+$xa;aP`Z9kwdpgT&G55 zj}V})iI&}Aw@7MOryctBH!Db&Wwym^fwS*^@wP%Sl)HF%C@B(2?J_^@iZt}4n0Ju1 z@xk+j|CE`v9RK2YV}buZW=?1yPywmiTmIDODsO=MNVu4t`w=E*pFsFc%YhFVVIZydA zW)=eNYE&YDD?cRd- zSibBT-TB~K-#4Uwj@<$MsV#W4G07Tv5UklM&W|oM4<{?9!-ssys<@?Tq)aI?zHvB} zliE>rid>;bo?XUMzTo^0oqJiidWMN=aQj!Y_ zfA8DdCB@S102?y?Pj`8r`KpK$gbpH!A?iU0Ig;Te z%hZB4@Jbe{HseJU1l%22rY4hp^G|Nvw4+gGq8U14ljgE3 zS6S$PDR2m0N)WQhMOipvDp@#UQCBakDZB9y+UBBBf5QuP!O_eQr^$by{E5LhS>~%n z%ll3Ntwm8)+WmYuTy{?TzB&OH?M zE&j@X;#Dk3e-Aa=X?1G4GM}_A3BKLHWTnJZE{#lKe|QkTCPc8#2A2G()!T$O$XVfA z6t`UqUs2t!7I=IL70e!jAkIb(AMU*(6&@eotVWmElE-5F#y@{qMn-ndU<=bJOEX`| zku^;b`@6)aR(SI<(#mye2l!w6p;r1IR6KSz5JX6W7YfZeWKc^UJP$-S)w#QG1$pNO=}|XE2iyguJ{8+&LP)o z<(!@1KL}act5x=V{=g_k$5^^bqcj`FXCmW+Emvit2RS@Eo`#g$TARzUNaf31F%Wed z480d05XCh(Ufi!XsLr{!KE7F-j9EW?+P`fOn@3y}{zI*99=LC)Bu*rgZv&j1Mc29w z8nC9M1Fo1{wCYv(@my8}EHaI<?&Ruoo}2xCxlV%$4w{I0lA{a=)_7XbBe z0YwXzJuyQI*wY&O=z+DJc8soJa5iF) z&$pEUlPO_roeH&+PSr$W^bBhD)S zO#q(q*Has??#j4mus`oALB%i4xTfJ$_~C}sWgx9AE&{P{9IpB-jcQ0Lq^mM#BB7>o z7wqZsy(q%q9Qn&)OWeBulmn()b>PpXREzrMm(D!+dlw7lPmpMoN}3f zC|fXKS0QaDm^bQ+J*_;%@O;$VZ#NT3L`Gdi=gfX~=vwN|*_G#+q^h=6Q3t6GRR16b zaM$mj=C^LaLR+2#B#+sePgFZWfnnDGGl7+;T5~&aDG!L3u9T1c(W~OhiRkz*XVuF% z#*lI%`mUYux+cO+QCM*He84Ucee&H+0JZ0@Uukwsz1ymmeJer3X#0S_1itK{zT}~1 zw@LXl9{lIYOS6|PdKR|=`~zfu3gAB4_(=_^K8I*d6qfy^DOnF=g)U)p56yG8Qho2B zO5l=|O+%!IGxSSPPh^V8*+n@0KaJgZ|J83`nhfP0WqH&jx>c1Ni4LaTVHW%aJw5TY zr;>w*01KZ{%pkw+zP!J?wZYr~2q5L){9lql zOUa2WV5DWipT5UowkYTL+TL~i={%Uxwc*@|kCW!n8^n_y?59F&t(BSR*r8NHtKWM3 zo?QMLbLzq)rMJwn)I$_wq4>w8CEtT#^Fqq4W?V_`d-PZf+IWomG>wXqc}2cAMc?}K z*MKs$BUqP>1z86gZYMwdgzU!x|7FXgfR_-(q_&a{?3$EXFa7M2<8i81-l5lkAK8u% zl7y*nXEQN7Y|9et-Ju&o8Y55HH{_J~sTLB)~ zR@$)hhmZ*>rU7Aj)&hucyqn0pQ0RyFYw&{mwSQ*@WCw4|-6>unJb7MCPeRY|B&^PS z!Kc|6(RqEno zIm8xb+(iuSX((gV(Jjjp4^R(%>2~~BeIt-P%{pK5GqBEB5l~~z`s4DC0SO`$&R5MU z*|HQKrnMTc$a@1Zd)e8MUh=VsU&nhpYGrMInC7X~jY!fspWat6H8z!ab!K(RZ~EZm z3;fRKcsA-Azp2FoPifCfy$Wur;=lEf$J1&CXEzmlkcY1*al7JfsY=rZw+Oz7c6?t~ znlBCZ%IT^nHMvmcABf>&c)_CLgr}3AVxz8Ka@rGnu_NstgRr>vYw5wC3$uye&*32OAB;shptvy~LEj|E1cm+70QS<0U{+ox!N5%b zIL7uGomso+1=!LazM^m8*j1uA14_j~W8+jGgyb-@a;mL&XH-!c)I`{5B1sw=Kgd=1 z&g<*ZpS{;Z5|595AIJ0-!X=*%yxm(pFR`i7Mp|>nN6KBw+qW(c9g1oU z{}Y8?{oh{t6MB%ZgYR8vG>PI0492+4f#`9@>a6cuf9Nc@j5dCf|Y_idz zBTzi4M+zDHnZv7>-%hf3)eNBzWjjwUptZb+`!%$Ob6rH&CCMx69;D8H3`n7Z`veNk z6Fhd@R6#|J#k~zi|9O!wZf?ND+#CV>)Q)AMg

BY-GW$Y*P9=OuXoF=8i`#FMkNZe6aKXOKx2%Am%F^=2gy+5Ewhd0u8gU%>ys}{TdwYV zkRJUz#pf1J;(%7CI$gvRxAC5CbwceIi_A6t(&!$Px!zS)n5LYHL5#Rgf5vIWh$3_* z&Vx||&NC@=H8e%&UM6p1+fC&6xBgv;xWtI;y&NO>k~!Ns020RHGrDUf_qst*HM=(m zu6rgTB8f_n@B^B_pMQf^i-hnsiC(S8d02G?a)RsXWVp5=6@Po&RTaCnwdB67j0*}_4Pm=vU44gIZWWde%vL})KOJi;Lr7QnisLUG~+a8Sc z*t1?JY}vzSz^lKO(~L589lk=?OuXkXsM4#!L1bfO!XLqRmQK>;um1066uw@sgx&$< zFlF_q9*>n{*uNNvB(=GQ@YH|!eU^G^XTGI~O|_vOeeSzTHGJ>}J4i4PrP7}hah*U* zV;V^CcJLO{-M92|T4Fi-?bh*Lt3R-n^w>z=pJ8Svp?68)JE<3E;ttQIPYPcB|5*D} aZVHtB`teJ8rGp~C`7+Ws)2q>OivB;s@UL Date: Fri, 27 Jun 2025 20:38:28 +0200 Subject: [PATCH 189/661] add slogan --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 09e2aff0..e356a472 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Vera +Because UI should be simple. Vera is a simple yet powerful Fabric UI library. There are currently no plans for a Forge version.\ [Submit a Logo](https://github.com/snackbag/vera/issues/new) [Visit Wiki](https://wiki.snackbag.net/w/vera) \ No newline at end of file From b05d47b76594014d2365539dcee99a9e296061b9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:09:12 +0200 Subject: [PATCH 190/661] basic dragging handler --- .../net/snackbag/vera/util/DragHandler.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/util/DragHandler.java diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java new file mode 100644 index 00000000..8efbb08d --- /dev/null +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -0,0 +1,75 @@ +package net.snackbag.vera.util; + +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VMouseButton; +import net.snackbag.vera.event.VMouseDragEvent; +import net.snackbag.vera.widget.VWidget; +import org.joml.Vector2i; + +public class DragHandler { + public static VMouseButton button = null; + public static VWidget target = null; + public static Vector2i beginPos = null; + + public static VMouseDragEvent.Context prevContext = null; + + public static boolean isDragging() { + return button != null && target != null; + } + + public static void down(VMouseButton button, VWidget target) { + if (isDragging()) return; + + DragHandler.button = button; + DragHandler.target = target; + beginPos = new Vector2i(Vera.getMouseX(), Vera.getMouseY()); + + fireEvents(); + } + + public static void move() { + if (!isDragging()) return; + + fireEvents(); + } + + public static void release(VMouseButton button) { + if (!isDragging()) return; + if (button != DragHandler.button) return; + + DragHandler.target = null; + DragHandler.button = null; + } + + public static VMouseDragEvent.Context createContext() { + if (!isDragging()) throw new UnsupportedOperationException("Cannot create mouse drag context when not dragging"); + + int x = Vera.getMouseX(); + int y = Vera.getMouseY(); + + Vector2i move = createMove(); + + return new VMouseDragEvent.Context( + beginPos.x, beginPos.y, + x, y, + move.x, move.y, + VMouseDragEvent.Direction.UP + ); + } + + private static Vector2i createMove() { + if (prevContext == null) return new Vector2i(0, 0); + else return new Vector2i( + Vera.getMouseX() - prevContext.currentX(), + Vera.getMouseY() - prevContext.currentY() + ); + } + + private static void fireEvents() { + switch (button) { + case LEFT -> target.events.fire("mouse-drag-left", createContext()); + case MIDDLE -> target.events.fire("mouse-drag-middle", createContext()); + case RIGHT -> target.events.fire("mouse-drag-right", createContext()); + } + } +} From f70268c3266a7b524ef439d295ebf1037465d27f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:09:22 +0200 Subject: [PATCH 191/661] call move for DragHandler --- src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index a100815a..5c8280ff 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -5,6 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -37,6 +38,8 @@ public abstract class MouseMixin { if (widget != null) widget.events.fire("mouse-move", mouseX, mouseY); else if (app.getCursorShape() != VCursorShape.DEFAULT) app.setCursorShape(VCursorShape.DEFAULT); }); + + DragHandler.move(); } @Inject(method = "onFilesDropped", at = @At("HEAD")) From 54f2fbe15ce57f9cf944b2b0d028c6f7561422a7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:09:48 +0200 Subject: [PATCH 192/661] call down and release for DragHandler aswell as turning the int buttons to VMouseButtons --- .../mcvera/mixin/ParentElementMixin.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 7e1f9e86..fa58bc4a 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -3,7 +3,9 @@ import net.minecraft.client.gui.ParentElement; import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VMouseButton; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -20,6 +22,8 @@ public interface ParentElementMixin { int mouseY = (int) mouseYRaw; boolean justChanged = false; + VMouseButton btn = VMouseButton.fromInt(button); + VeraApp top = MCVeraData.getTopHierarchy(); for (VeraApp app : MCVeraData.appHierarchy) { @@ -35,28 +39,29 @@ public interface ParentElementMixin { if (!app.isPointOverThis(mouseX, mouseY)) return; if (finalJustChanged) return; - handleClickEvents(app.getTopWidgetAt(mouseX, mouseY), button); + handleClickEvents(app.getTopWidgetAt(mouseX, mouseY), btn); }); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; VWidget hoveredWidget = app.getTopWidgetAt(mouseX, mouseY); - if (hoveredWidget != null) handleClickEvents(hoveredWidget, button); + if (hoveredWidget != null) handleClickEvents(hoveredWidget, btn); else app.setFocusedWidget(null); }); } @Unique - private void handleClickEvents(@Nullable VWidget widget, int button) { + private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) { if (widget == null) return; switch (button) { - case 0 -> widget.events.fire("left-click"); - case 1 -> widget.events.fire("right-click"); - case 2 -> widget.events.fire("middle-click"); - default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); + case LEFT -> widget.events.fire("left-click"); + case RIGHT -> widget.events.fire("right-click"); + case MIDDLE -> widget.events.fire("middle-click"); } + + DragHandler.down(button, widget); } @Inject(method = "mouseReleased", at = @At("HEAD")) @@ -64,24 +69,27 @@ private void handleClickEvents(@Nullable VWidget widget, int button) { int mouseX = (int) mouseXRaw; int mouseY = (int) mouseYRaw; - MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), button)); + VMouseButton btn = VMouseButton.fromInt(button); + + MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), btn)); Vera.forAllVisibleApps(app -> { if (app.isRequiresHierarchy()) return; if (!app.isPointOverThis(mouseX, mouseY)) return; - handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), button); + handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), btn); }); + + DragHandler.release(btn); } @Unique - private void handleReleaseEvents(@Nullable VWidget widget, int button) { + private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton button) { if (widget == null) return; switch (button) { - case 0 -> widget.events.fire("left-click-release"); - case 1 -> widget.events.fire("right-click-release"); - case 2 -> widget.events.fire("middle-click-release"); - default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); + case LEFT -> widget.events.fire("left-click-release"); + case RIGHT -> widget.events.fire("right-click-release"); + case MIDDLE -> widget.events.fire("middle-click-release"); } } From f2d2c8e28ca25971a999e10b0ba8b3078211ca8b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:09:53 +0200 Subject: [PATCH 193/661] add fromInt method --- .../java/net/snackbag/vera/core/VMouseButton.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VMouseButton.java b/src/main/java/net/snackbag/vera/core/VMouseButton.java index 8c9ef743..c8d5e5d1 100644 --- a/src/main/java/net/snackbag/vera/core/VMouseButton.java +++ b/src/main/java/net/snackbag/vera/core/VMouseButton.java @@ -3,5 +3,14 @@ public enum VMouseButton { LEFT, MIDDLE, - RIGHT + RIGHT; + + public static VMouseButton fromInt(int button) { + return switch (button) { + case 0 -> LEFT; + case 1 -> RIGHT; + case 2 -> MIDDLE; + default -> throw new IllegalArgumentException("Invalid button type: %d".formatted(button)); + }; + } } From 5b7d99bb9c3c47dbce1a3f38b6c1a6a9aba74c68 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:11:02 +0200 Subject: [PATCH 194/661] round mouse coords for more accuracy --- .../java/net/snackbag/mcvera/mixin/MouseMixin.java | 4 ++-- .../snackbag/mcvera/mixin/ParentElementMixin.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index 5c8280ff..d543d4df 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -27,8 +27,8 @@ public abstract class MouseMixin { double scaleFactor = client.getWindow().getScaleFactor(); - int mouseX = (int) (fx / scaleFactor); - int mouseY = (int) (fy / scaleFactor); + int mouseX = (int) Math.round(fx / scaleFactor); + int mouseY = (int) Math.round(fy / scaleFactor); VeraApp top = Vera.getTopHierarchyApp(); Vera.forAllVisibleApps(app -> { diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index fa58bc4a..e8d401c5 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -18,8 +18,8 @@ public interface ParentElementMixin { @Inject(method = "mouseClicked", at = @At("HEAD")) private void mcvera$handleMouseClick(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { - int mouseX = (int) mouseXRaw; - int mouseY = (int) mouseYRaw; + int mouseX = (int) Math.round(mouseXRaw); + int mouseY = (int) Math.round(mouseYRaw); boolean justChanged = false; VMouseButton btn = VMouseButton.fromInt(button); @@ -66,8 +66,8 @@ private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) @Inject(method = "mouseReleased", at = @At("HEAD")) private void mcvera$handleMouseRelease(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { - int mouseX = (int) mouseXRaw; - int mouseY = (int) mouseYRaw; + int mouseX = (int) Math.round(mouseXRaw); + int mouseY = (int) Math.round(mouseYRaw); VMouseButton btn = VMouseButton.fromInt(button); @@ -95,8 +95,8 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto @Inject(method = "mouseScrolled", at = @At("HEAD")) private void mcvera$handleMouseScroll(double mouseXRaw, double mouseYRaw, double amount, CallbackInfoReturnable cir) { - int mouseX = (int) mouseXRaw; - int mouseY = (int) mouseYRaw; + int mouseX = (int) Math.round(mouseXRaw); + int mouseY = (int) Math.round(mouseYRaw); MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount)); Vera.forAllVisibleApps(app -> { From 95e225ab85667b8b90828e145222fc1b5aa30884 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:18:37 +0200 Subject: [PATCH 195/661] use class --- src/main/java/net/snackbag/vera/util/DragHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java index 8efbb08d..87c51592 100644 --- a/src/main/java/net/snackbag/vera/util/DragHandler.java +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -22,7 +22,7 @@ public static void down(VMouseButton button, VWidget target) { DragHandler.button = button; DragHandler.target = target; - beginPos = new Vector2i(Vera.getMouseX(), Vera.getMouseY()); + DragHandler.beginPos = new Vector2i(Vera.getMouseX(), Vera.getMouseY()); fireEvents(); } From a836df175a37da51fddf1fa2debdb4ef1817716e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:18:42 +0200 Subject: [PATCH 196/661] fully reset on release --- src/main/java/net/snackbag/vera/util/DragHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java index 87c51592..814ddb51 100644 --- a/src/main/java/net/snackbag/vera/util/DragHandler.java +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -39,6 +39,8 @@ public static void release(VMouseButton button) { DragHandler.target = null; DragHandler.button = null; + DragHandler.beginPos = null; + DragHandler.prevContext = null; } public static VMouseDragEvent.Context createContext() { From 642825cff9342b2b78529a2857db86d9708f8ef5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:18:48 +0200 Subject: [PATCH 197/661] update prevContext --- src/main/java/net/snackbag/vera/util/DragHandler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java index 814ddb51..375b3821 100644 --- a/src/main/java/net/snackbag/vera/util/DragHandler.java +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -50,13 +50,15 @@ public static VMouseDragEvent.Context createContext() { int y = Vera.getMouseY(); Vector2i move = createMove(); - - return new VMouseDragEvent.Context( + VMouseDragEvent.Context ctx = new VMouseDragEvent.Context( beginPos.x, beginPos.y, x, y, move.x, move.y, VMouseDragEvent.Direction.UP ); + + prevContext = ctx; + return ctx; } private static Vector2i createMove() { From d585bf606cdca46f0fa7eb81fc12565c99f05027 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:19:26 +0200 Subject: [PATCH 198/661] add clear method --- src/main/java/net/snackbag/vera/util/DragHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java index 375b3821..f1563abf 100644 --- a/src/main/java/net/snackbag/vera/util/DragHandler.java +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -37,6 +37,10 @@ public static void release(VMouseButton button) { if (!isDragging()) return; if (button != DragHandler.button) return; + clear(); + } + + public static void clear() { DragHandler.target = null; DragHandler.button = null; DragHandler.beginPos = null; From 2d2b05914d5e22a897c113dcec1271997a8680ec Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:22:52 +0200 Subject: [PATCH 199/661] fix dragging hover off and on --- src/main/java/net/snackbag/vera/widget/VWidget.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 4ec51511..78f7846b 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -6,6 +6,7 @@ import net.snackbag.vera.event.*; import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.util.DragHandler; import java.nio.file.Path; import java.util.*; @@ -88,6 +89,14 @@ public StyleState createStyleState() { else if (middleClickDown) return StyleState.MIDDLE_CLICKED; else if (rightClickDown) return StyleState.RIGHT_CLICKED; + else if (DragHandler.isDragging() && DragHandler.target == this) { + return switch (DragHandler.button) { + case LEFT -> StyleState.LC_DRAGGING; + case MIDDLE -> StyleState.MC_DRAGGING; + case RIGHT -> StyleState.RC_DRAGGING; + }; + } + // Hover as last, since everything else is hover too else if (isHovered()) return StyleState.HOVERED; else return StyleState.DEFAULT; From 54571c8898ff291645a94fb82e421bc8a81c3a73 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:33:42 +0200 Subject: [PATCH 200/661] move VAlignmentFlag to flag package --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- .../java/net/snackbag/vera/{core => flag}/VAlignmentFlag.java | 2 +- src/main/java/net/snackbag/vera/widget/VLabel.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename src/main/java/net/snackbag/vera/{core => flag}/VAlignmentFlag.java (66%) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 240e629f..5f464a4e 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -2,7 +2,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VAlignmentFlag; +import net.snackbag.vera.flag.VAlignmentFlag; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; diff --git a/src/main/java/net/snackbag/vera/core/VAlignmentFlag.java b/src/main/java/net/snackbag/vera/flag/VAlignmentFlag.java similarity index 66% rename from src/main/java/net/snackbag/vera/core/VAlignmentFlag.java rename to src/main/java/net/snackbag/vera/flag/VAlignmentFlag.java index df5ca8b8..18edb37e 100644 --- a/src/main/java/net/snackbag/vera/core/VAlignmentFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VAlignmentFlag.java @@ -1,4 +1,4 @@ -package net.snackbag.vera.core; +package net.snackbag.vera.flag; public enum VAlignmentFlag { LEFT, diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index bc1996a6..5e2d4ad1 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -2,6 +2,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; +import net.snackbag.vera.flag.VAlignmentFlag; import net.snackbag.vera.modifier.VPaddingWidget; import net.snackbag.vera.style.StyleState; From 9c4795755f9256bdca53fe4a2ca13f07af5bf68e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:34:31 +0200 Subject: [PATCH 201/661] add VVAlignmentFlag --- src/main/java/net/snackbag/vera/flag/VVAlignmentFlag.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/flag/VVAlignmentFlag.java diff --git a/src/main/java/net/snackbag/vera/flag/VVAlignmentFlag.java b/src/main/java/net/snackbag/vera/flag/VVAlignmentFlag.java new file mode 100644 index 00000000..d98feff6 --- /dev/null +++ b/src/main/java/net/snackbag/vera/flag/VVAlignmentFlag.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.flag; + +public enum VVAlignmentFlag { + TOP, + CENTER, + BOTTOM +} From 4e6dfea0ce3d70ccfbd1beca8da87828fce4d957 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:34:46 +0200 Subject: [PATCH 202/661] rename VAlignmentFlag to VHAlignmentFlag --- .../java/net/snackbag/mcvera/test/TestApplication.java | 6 +++--- .../flag/{VAlignmentFlag.java => VHAlignmentFlag.java} | 2 +- src/main/java/net/snackbag/vera/widget/VLabel.java | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/net/snackbag/vera/flag/{VAlignmentFlag.java => VHAlignmentFlag.java} (69%) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 5f464a4e..f08a4d75 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -2,7 +2,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; -import net.snackbag.vera.flag.VAlignmentFlag; +import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; @@ -71,7 +71,7 @@ public void init() { }); VLabel centerLabel = new VLabel("CENTER", this).alsoAdd(); - centerLabel.setAlignment(VAlignmentFlag.CENTER); + centerLabel.setAlignment(VHAlignmentFlag.CENTER); centerLabel.setStyle("background-color", VColor.black()); centerLabel.modifyFontColor("font").rgb(255, 255, 255); centerLabel.move(220, 10); @@ -80,7 +80,7 @@ public void init() { centerLabel.setStyle("cursor", VCursorShape.ALL_RESIZE); VLabel rightLabel = new VLabel("RIGHT", this).alsoAdd(); - rightLabel.setAlignment(VAlignmentFlag.RIGHT); + rightLabel.setAlignment(VHAlignmentFlag.RIGHT); rightLabel.setStyle("background-color", VColor.black()); rightLabel.modifyFontColor("font").rgb(255, 255, 255); rightLabel.move(100, 10); diff --git a/src/main/java/net/snackbag/vera/flag/VAlignmentFlag.java b/src/main/java/net/snackbag/vera/flag/VHAlignmentFlag.java similarity index 69% rename from src/main/java/net/snackbag/vera/flag/VAlignmentFlag.java rename to src/main/java/net/snackbag/vera/flag/VHAlignmentFlag.java index 18edb37e..aaf09c5c 100644 --- a/src/main/java/net/snackbag/vera/flag/VAlignmentFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VHAlignmentFlag.java @@ -1,6 +1,6 @@ package net.snackbag.vera.flag; -public enum VAlignmentFlag { +public enum VHAlignmentFlag { LEFT, CENTER, RIGHT diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 5e2d4ad1..01246a5e 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -2,14 +2,14 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; -import net.snackbag.vera.flag.VAlignmentFlag; +import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.modifier.VPaddingWidget; import net.snackbag.vera.style.StyleState; public class VLabel extends VWidget implements VPaddingWidget { private String text; private V4Int padding; - private VAlignmentFlag alignment; + private VHAlignmentFlag alignment; public VLabel(String text, VeraApp app) { super(0, 0, 100, 16, app); @@ -17,7 +17,7 @@ public VLabel(String text, VeraApp app) { this.text = text; this.padding = new V4Int(4); this.focusOnClick = false; - alignment = VAlignmentFlag.LEFT; + alignment = VHAlignmentFlag.LEFT; setStyle("background-color", VColor.transparent()); setStyle("font", VFont.create()); @@ -63,11 +63,11 @@ public int getHitboxY() { return getY() - padding.get1(); } - public VAlignmentFlag getAlignment() { + public VHAlignmentFlag getAlignment() { return alignment; } - public void setAlignment(VAlignmentFlag alignment) { + public void setAlignment(VHAlignmentFlag alignment) { this.alignment = alignment; } From a5308153e57196d9a087c5e60224082d7655f3fe Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:48:17 +0200 Subject: [PATCH 203/661] setKey for classes --- .../net/snackbag/vera/style/VStyleSheet.java | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 79bb19df..c88abeee 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -63,12 +63,13 @@ public void setKey(VWidget widget, String key, Object object) { setKey(widget, key, object, StyleState.DEFAULT); } + public void setKey(String clazz, String key, Object object) { + setKey(clazz, key, object, StyleState.DEFAULT); + } + public void setKey(VWidget widget, String key, Object value, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; - if (value.getClass().isArray()) { - int length = Array.getLength(value); - if (length == 1) value = Array.get(value, 0); - } + value = potentiallyUnpackArray(value); StyleValueType res = getReservation(key); StyleValueType valRes = StyleValueType.get(value, res); @@ -85,6 +86,25 @@ public void setKey(VWidget widget, String key, Object value, @Nullable StyleS widgetSpecificStyles.get(widget).get(key).put(state, value); } + public void setKey(String clazz, String key, Object value, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + value = potentiallyUnpackArray(value); + + StyleValueType res = getReservation(key); + StyleValueType valRes = StyleValueType.get(value, res); + + if (res != null) { + if (valRes != res) + throw new RuntimeException("Cannot set key %s (for class %s), because it is reserved for type %s. Received: %s".formatted(key, clazz, res, valRes)); + } else reserveType(key, valRes); + + value = StyleValueType.convert(value, valRes); + + if (!styleClasses.containsKey(clazz)) styleClasses.put(clazz, new HashMap<>()); + if (!styleClasses.get(clazz).containsKey(key)) styleClasses.get(clazz).put(key, new HashMap<>()); + styleClasses.get(clazz).get(key).put(state, value); + } + public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { return new VColor.ColorModifier(getKey(widget, key), color -> setKey(widget, key, color)); } @@ -105,6 +125,15 @@ public HashMap> getClassStyles(String clazz) return styleClasses.getOrDefault(clazz, new HashMap<>()); } + private Object potentiallyUnpackArray(Object value) { + if (value.getClass().isArray()) { + int length = Array.getLength(value); + if (length == 1) value = Array.get(value, 0); + } + + return value; + } + public HashMap> mixClasses(LinkedHashSet classes) { final HashMap> values = new HashMap<>(); From 99e1d29a6b4aae10aa31bd563670a909ccb33269 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:48:30 +0200 Subject: [PATCH 204/661] test for classes and multiple stylesheets --- .../mcvera/test/StyleTestApplication.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index a655f846..fa8fd168 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -2,9 +2,11 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -15,9 +17,11 @@ public class StyleTestApplication extends VeraApp { public void init() { new VShortcut(this, "escape", this::hide).alsoAdd(); - //loadStyleSheet(new Identifier(MinecraftVera.MOD_ID, "demo/test.vss")); + mergeStyleSheet(createStyleSheet()); - new VLabel("helo", this).alsoAdd(); + new VLabel("helo", this) + .alsoAddClass("label") + .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); testRect.setStyle("color", StyleState.HOVERED, VColor.white()); @@ -27,4 +31,13 @@ public void init() { testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); } + + public VStyleSheet createStyleSheet() { + VStyleSheet sheet = new VStyleSheet(); + + sheet.setKey("label", "font", VFont.create()); + sheet.setKey("label", "font", VFont.create().withColor(VColor.of(20)), StyleState.HOVERED); + + return sheet; + } } From cd9c538858a5fe22f86874607093ec3601d55bc4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 21:48:36 +0200 Subject: [PATCH 205/661] alsoAddClass method --- src/main/java/net/snackbag/vera/widget/VWidget.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 78f7846b..f3d5052f 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -310,6 +310,11 @@ public void remove() { app.removeWidget(this); } + public T alsoAddClass(String clazz) { + classes.add(clazz); + return (T) this; + } + public T alsoAdd() { app.addWidget(this); return (T) this; From b32075d6052380ee5b51e76e053964393de02c2b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 22:07:19 +0200 Subject: [PATCH 206/661] fix dropdown not opening --- .../net/snackbag/vera/widget/VDropdown.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 4d7acae0..b713ef3d 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -153,8 +153,6 @@ public void setPadding(V4Int padding) { @Override public void handleBuiltinEvent(String event, Object... args) { - super.handleBuiltinEvent(event, args); - int x = getX(); int y = getY(); @@ -168,7 +166,6 @@ public void handleBuiltinEvent(String event, Object... args) { } setFocused(false); - return; } } @@ -181,7 +178,6 @@ public void handleBuiltinEvent(String event, Object... args) { } setFocused(false); - return; } } @@ -194,29 +190,28 @@ public void handleBuiltinEvent(String event, Object... args) { } setFocused(false); - return; } } case "mouse-move" -> { - if (!isFocused()) { - hoveredItem = null; - return; - } - - // Get mouse position relative to the dropdown's top-left corner - int argX = (int) args[0]; - int argY = (int) args[1]; + if (!isFocused()) hoveredItem = null; + else { + // Get mouse position relative to the dropdown's top-left corner + int argX = (int) args[0]; + int argY = (int) args[1]; - int mouseX = argX - x; - int mouseY = argY - y; + int mouseX = argX - x; + int mouseY = argY - y; - Item item = getItemAt(mouseX, mouseY); - hoveredItem = (item != null) ? items.indexOf(item) : null; + Item item = getItemAt(mouseX, mouseY); + hoveredItem = (item != null) ? items.indexOf(item) : null; + } } case "hover-leave" -> hoveredItem = null; } + + super.handleBuiltinEvent(event, args); } private int getItemIndexAt(int mouseY) { From 3203f38fdcfe2d9614d3b21df3d8504cefc5d50d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 22:11:25 +0200 Subject: [PATCH 207/661] allow method without adjusting size --- src/main/java/net/snackbag/vera/widget/VLabel.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 01246a5e..d9f00b43 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -11,8 +11,8 @@ public class VLabel extends VWidget implements VPaddingWidget { private V4Int padding; private VHAlignmentFlag alignment; - public VLabel(String text, VeraApp app) { - super(0, 0, 100, 16, app); + public VLabel(String text, int x, int y, int width, int height, VeraApp app) { + super(x, y, width, height, app); this.text = text; this.padding = new V4Int(4); @@ -21,6 +21,10 @@ public VLabel(String text, VeraApp app) { setStyle("background-color", VColor.transparent()); setStyle("font", VFont.create()); + } + + public VLabel(String text, VeraApp app) { + this(text, 0, 0, 100, 16, app); adjustSize(); } From 8a81ee934362135f6743cde79997b0cff688b308 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 22:11:36 +0200 Subject: [PATCH 208/661] fix sizes of rightLabel and centerLabel --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index f08a4d75..89102b70 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -70,20 +70,18 @@ public void init() { label.setText("Not hovered"); }); - VLabel centerLabel = new VLabel("CENTER", this).alsoAdd(); + VLabel centerLabel = new VLabel("CENTER", 220, 10, 100, 16, this).alsoAdd(); centerLabel.setAlignment(VHAlignmentFlag.CENTER); centerLabel.setStyle("background-color", VColor.black()); centerLabel.modifyFontColor("font").rgb(255, 255, 255); - centerLabel.move(220, 10); centerLabel.setStyle("border-color", VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); centerLabel.setStyle("border-size", 5, 10, 8, 16); centerLabel.setStyle("cursor", VCursorShape.ALL_RESIZE); - VLabel rightLabel = new VLabel("RIGHT", this).alsoAdd(); + VLabel rightLabel = new VLabel("RIGHT", 100, 10, 100, 16, this).alsoAdd(); rightLabel.setAlignment(VHAlignmentFlag.RIGHT); rightLabel.setStyle("background-color", VColor.black()); rightLabel.modifyFontColor("font").rgb(255, 255, 255); - rightLabel.move(100, 10); rightLabel.setStyle("border-color", VColor.white()); rightLabel.setStyle("border-size", 1); rightLabel.onRightClick(() -> System.out.println(Vera.openFileSelector("test", Path.of("/Volumes/Media"), null))); From 0a037aef4b9ed3bf528f4806952351e36c08d545 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 22:14:26 +0200 Subject: [PATCH 209/661] revert rounding --- .../java/net/snackbag/mcvera/mixin/MouseMixin.java | 4 ++-- .../snackbag/mcvera/mixin/ParentElementMixin.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index d543d4df..5c8280ff 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -27,8 +27,8 @@ public abstract class MouseMixin { double scaleFactor = client.getWindow().getScaleFactor(); - int mouseX = (int) Math.round(fx / scaleFactor); - int mouseY = (int) Math.round(fy / scaleFactor); + int mouseX = (int) (fx / scaleFactor); + int mouseY = (int) (fy / scaleFactor); VeraApp top = Vera.getTopHierarchyApp(); Vera.forAllVisibleApps(app -> { diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index e8d401c5..fa58bc4a 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -18,8 +18,8 @@ public interface ParentElementMixin { @Inject(method = "mouseClicked", at = @At("HEAD")) private void mcvera$handleMouseClick(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { - int mouseX = (int) Math.round(mouseXRaw); - int mouseY = (int) Math.round(mouseYRaw); + int mouseX = (int) mouseXRaw; + int mouseY = (int) mouseYRaw; boolean justChanged = false; VMouseButton btn = VMouseButton.fromInt(button); @@ -66,8 +66,8 @@ private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) @Inject(method = "mouseReleased", at = @At("HEAD")) private void mcvera$handleMouseRelease(double mouseXRaw, double mouseYRaw, int button, CallbackInfoReturnable cir) { - int mouseX = (int) Math.round(mouseXRaw); - int mouseY = (int) Math.round(mouseYRaw); + int mouseX = (int) mouseXRaw; + int mouseY = (int) mouseYRaw; VMouseButton btn = VMouseButton.fromInt(button); @@ -95,8 +95,8 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto @Inject(method = "mouseScrolled", at = @At("HEAD")) private void mcvera$handleMouseScroll(double mouseXRaw, double mouseYRaw, double amount, CallbackInfoReturnable cir) { - int mouseX = (int) Math.round(mouseXRaw); - int mouseY = (int) Math.round(mouseYRaw); + int mouseX = (int) mouseXRaw; + int mouseY = (int) mouseYRaw; MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount)); Vera.forAllVisibleApps(app -> { From f1660d8c8b98fd36e17c97f24d8609c7d289db7d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 22:18:21 +0200 Subject: [PATCH 210/661] fix dropdown not defocusing when clicking --- src/main/java/net/snackbag/vera/widget/VDropdown.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index b713ef3d..04401405 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -166,6 +166,7 @@ public void handleBuiltinEvent(String event, Object... args) { } setFocused(false); + return; } } @@ -178,6 +179,7 @@ public void handleBuiltinEvent(String event, Object... args) { } setFocused(false); + return; } } @@ -190,6 +192,7 @@ public void handleBuiltinEvent(String event, Object... args) { } setFocused(false); + return; } } From 1ae40b3db2633ce8f67b5ec7e165b64a6b46779a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Jun 2025 22:18:46 +0200 Subject: [PATCH 211/661] add TODO --- src/main/java/net/snackbag/vera/widget/VDropdown.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 04401405..3e78802c 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; +// TODO: Rewrite VDropdown from scratch public class VDropdown extends VWidget implements VPaddingWidget { private final List items; private VFont font; From d2664de21c3c9e6c18049e5c05f97cb6c8aa3e02 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 14:19:19 +0200 Subject: [PATCH 212/661] more visible change --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index fa8fd168..c6f155e6 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -36,7 +36,7 @@ public VStyleSheet createStyleSheet() { VStyleSheet sheet = new VStyleSheet(); sheet.setKey("label", "font", VFont.create()); - sheet.setKey("label", "font", VFont.create().withColor(VColor.of(20)), StyleState.HOVERED); + sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD), StyleState.HOVERED); return sheet; } From 6631702cb813f7e3d3e8ba332114fe7ad62188a9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 14:19:25 +0200 Subject: [PATCH 213/661] add mergeStyleSheet method --- src/main/java/net/snackbag/vera/core/VeraApp.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 28158cbe..73fa2a78 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -333,4 +333,8 @@ public void keyPressed(int keyCode, int scanCode, int modifiers) { public void charTyped(char chr, int modifiers) { if (hasFocusedWidget()) getFocusedWidget().charTyped(chr, modifiers); } + + public void mergeStyleSheet(VStyleSheet target) { + styleSheet.addSheet(target); + } } From 3e4b326524042003a4d7d4a1a0a733f79e386183 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 14:19:30 +0200 Subject: [PATCH 214/661] no longer final --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index c88abeee..52efdbdc 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -10,9 +10,9 @@ import java.util.LinkedHashSet; public class VStyleSheet { - private final HashMap, HashMap>> widgetSpecificStyles = new HashMap<>(); - private final HashMap>> styleClasses = new HashMap<>(); - private final HashMap typeRegistry = new HashMap<>(); + private HashMap, HashMap>> widgetSpecificStyles = new HashMap<>(); + private HashMap>> styleClasses = new HashMap<>(); + private HashMap typeRegistry = new HashMap<>(); public VStyleSheet() { // Generic From 56377bcd8f7ca9fb86a78766a470868145705b50 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 14:19:48 +0200 Subject: [PATCH 215/661] stylesheet merging functionality --- .../net/snackbag/vera/style/VStyleSheet.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 52efdbdc..26a9ae58 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -134,6 +134,66 @@ private Object potentiallyUnpackArray(Object value) { return value; } + public void addSheet(VStyleSheet target) { + // Merge type registries + HashMap mergedTypeRegistry = new HashMap<>(typeRegistry); + + for (String key : target.typeRegistry.keySet()) { + StyleValueType type = target.typeRegistry.get(key); + + if (!mergedTypeRegistry.containsKey(key)) mergedTypeRegistry.put(key, type); + else if (mergedTypeRegistry.get(key) != type) + throw new UnsupportedOperationException("Cannot merge two sheets with different type registry entries. Received %s:%s; already %s".formatted(key, type, mergedTypeRegistry.get(key))); + } + + // Merge style classes + HashMap>> mergedStyleClasses = new HashMap<>(styleClasses); + + for (String clazz : target.styleClasses.keySet()) { + for (String key : target.styleClasses.get(clazz).keySet()) { + for (StyleState state : target.styleClasses.get(clazz).get(key).keySet()) { + if (!mergedStyleClasses.containsKey(clazz)) { + mergedStyleClasses.put(clazz, target.styleClasses.get(clazz)); + continue; + } + + if (!mergedStyleClasses.get(clazz).containsKey(key)) { + mergedStyleClasses.get(clazz).put(key, target.styleClasses.get(clazz).get(key)); + continue; + } + + mergedStyleClasses.get(clazz).get(key).put(state, target.styleClasses.get(clazz).get(key).get(state)); + } + } + } + + // Merge widget specific styles + HashMap, HashMap>> mergedWidgetSpecificStyles = new HashMap<>(widgetSpecificStyles); + + for (VWidget widget : target.widgetSpecificStyles.keySet()) { + for (String key : target.widgetSpecificStyles.get(widget).keySet()) { + for (StyleState state : target.widgetSpecificStyles.get(widget).get(key).keySet()) { + if (!mergedWidgetSpecificStyles.containsKey(widget)) { + mergedWidgetSpecificStyles.put(widget, target.widgetSpecificStyles.get(widget)); + continue; + } + + if (!mergedWidgetSpecificStyles.get(widget).containsKey(key)) { + mergedWidgetSpecificStyles.get(widget).put(key, target.widgetSpecificStyles.get(widget).get(key)); + continue; + } + + mergedWidgetSpecificStyles.get(widget).get(key).put(state, target.widgetSpecificStyles.get(widget).get(key).get(state)); + } + } + } + + // Apply changes if everything went fine + typeRegistry = mergedTypeRegistry; + styleClasses = mergedStyleClasses; + widgetSpecificStyles = mergedWidgetSpecificStyles; + } + public HashMap> mixClasses(LinkedHashSet classes) { final HashMap> values = new HashMap<>(); From 0f4d0a5536a6709be5338a20336c567b8e5b4357 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 16:09:41 +0200 Subject: [PATCH 216/661] HashMaps to StyleContainers --- .../snackbag/vera/style/StyleContainer.java | 75 ++++++++++++++++++ .../net/snackbag/vera/style/VStyleSheet.java | 77 ++++--------------- 2 files changed, 90 insertions(+), 62 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/StyleContainer.java diff --git a/src/main/java/net/snackbag/vera/style/StyleContainer.java b/src/main/java/net/snackbag/vera/style/StyleContainer.java new file mode 100644 index 00000000..e0be4491 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/StyleContainer.java @@ -0,0 +1,75 @@ +package net.snackbag.vera.style; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; + +/** + * Holds types, keys and states. + *

+ * Consists of:
+ * | Part
+ * |--- Key
+ * |------ StyleState:Object + */ +public class StyleContainer { + private final HashMap>> values = new HashMap<>(); + + public StyleContainer() {} + + public boolean hasPart(T part) { + return values.containsKey(part); + } + + public HashMap> getPart(T part) { + return values.getOrDefault(part, new HashMap<>()); + } + + public boolean hasKey(T part, String key) { + return getPart(part).containsKey(key); + } + + public HashMap getKey(T part, String key) { + return getPart(part).getOrDefault(key, new HashMap<>()); + } + + public boolean hasState(T part, String key, StyleState state) { + return getKey(part, key).containsKey(state); + } + + public V getState(T part, String key, StyleState state) { + return (V) getKey(part, key).get(state); + } + + public void put(T part, String key, StyleState state, Object value) { + if (!hasPart(part)) values.put(part, new HashMap<>()); + if (!hasKey(part, key)) values.get(part).put(key, new HashMap<>()); + if (!hasState(part, key, state)) values.get(part).get(key).put(state, new HashMap<>()); + + values.get(part).get(key).put(state, value); + } + + public void moldWith(StyleContainer target) { + for (T targetPart : target.values.keySet()) { + if (!hasPart(targetPart)) { + values.put(targetPart, target.getPart(targetPart)); + continue; + } + + for (String targetKey : target.getPart(targetPart).keySet()) { + if (!hasKey(targetPart, targetKey)) { + values.get(targetPart).put(targetKey, target.getKey(targetPart, targetKey)); + continue; + } + + for (StyleState targetState : target.getKey(targetPart, targetKey).keySet()) { + if (!hasState(targetPart, targetKey, targetState)) { + values.get(targetPart).get(targetKey).put(targetState, target.getState(targetPart, targetKey, targetState)); + continue; + } + } + } + } + } +} diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 26a9ae58..e6fc13cf 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -10,8 +10,8 @@ import java.util.LinkedHashSet; public class VStyleSheet { - private HashMap, HashMap>> widgetSpecificStyles = new HashMap<>(); - private HashMap>> styleClasses = new HashMap<>(); + private final StyleContainer> widgetSpecificStyles = new StyleContainer<>(); + private final StyleContainer styleClasses = new StyleContainer<>(); private HashMap typeRegistry = new HashMap<>(); public VStyleSheet() { @@ -44,11 +44,6 @@ public T getKey(VWidget widget, String key) { public T getKey(VWidget widget, String key, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; - if (widgetSpecificStyles.containsKey(widget) && widgetSpecificStyles.get(widget).containsKey(key)) { - if (!widgetSpecificStyles.get(widget).get(key).containsKey(state)) return getKey(widget, key, state.fallback); - return (T) widgetSpecificStyles.get(widget).get(key).get(state); - } - HashMap> mixed = mixClasses(widget.classes); if (mixed.containsKey(key)) { @@ -56,6 +51,14 @@ public T getKey(VWidget widget, String key, @Nullable StyleState state) { return (T) mixed.get(key).get(state); } + // if widget contains key + if (widgetSpecificStyles.hasKey(widget, key)) { + // if it doesn't contain the state return with the fallback + if (!widgetSpecificStyles.hasState(widget, key, state)) return getKey(widget, key, state.fallback); + return widgetSpecificStyles.getState(widget, key, state); + } + + // if nothing worked, return null return null; } @@ -81,9 +84,7 @@ public void setKey(VWidget widget, String key, Object value, @Nullable StyleS value = StyleValueType.convert(value, valRes); - if (!widgetSpecificStyles.containsKey(widget)) widgetSpecificStyles.put(widget, new HashMap<>()); - if (!widgetSpecificStyles.get(widget).containsKey(key)) widgetSpecificStyles.get(widget).put(key, new HashMap<>()); - widgetSpecificStyles.get(widget).get(key).put(state, value); + widgetSpecificStyles.put(widget, key, state, value); } public void setKey(String clazz, String key, Object value, @Nullable StyleState state) { @@ -100,9 +101,7 @@ public void setKey(String clazz, String key, Object value, @Nullable StyleState value = StyleValueType.convert(value, valRes); - if (!styleClasses.containsKey(clazz)) styleClasses.put(clazz, new HashMap<>()); - if (!styleClasses.get(clazz).containsKey(key)) styleClasses.get(clazz).put(key, new HashMap<>()); - styleClasses.get(clazz).get(key).put(state, value); + styleClasses.put(clazz, key, state, value); } public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { @@ -121,10 +120,6 @@ public void reserveType(String key, StyleValueType type) { return typeRegistry.getOrDefault(key, null); } - public HashMap> getClassStyles(String clazz) { - return styleClasses.getOrDefault(clazz, new HashMap<>()); - } - private Object potentiallyUnpackArray(Object value) { if (value.getClass().isArray()) { int length = Array.getLength(value); @@ -146,59 +141,17 @@ else if (mergedTypeRegistry.get(key) != type) throw new UnsupportedOperationException("Cannot merge two sheets with different type registry entries. Received %s:%s; already %s".formatted(key, type, mergedTypeRegistry.get(key))); } - // Merge style classes - HashMap>> mergedStyleClasses = new HashMap<>(styleClasses); - - for (String clazz : target.styleClasses.keySet()) { - for (String key : target.styleClasses.get(clazz).keySet()) { - for (StyleState state : target.styleClasses.get(clazz).get(key).keySet()) { - if (!mergedStyleClasses.containsKey(clazz)) { - mergedStyleClasses.put(clazz, target.styleClasses.get(clazz)); - continue; - } - - if (!mergedStyleClasses.get(clazz).containsKey(key)) { - mergedStyleClasses.get(clazz).put(key, target.styleClasses.get(clazz).get(key)); - continue; - } - - mergedStyleClasses.get(clazz).get(key).put(state, target.styleClasses.get(clazz).get(key).get(state)); - } - } - } - - // Merge widget specific styles - HashMap, HashMap>> mergedWidgetSpecificStyles = new HashMap<>(widgetSpecificStyles); - - for (VWidget widget : target.widgetSpecificStyles.keySet()) { - for (String key : target.widgetSpecificStyles.get(widget).keySet()) { - for (StyleState state : target.widgetSpecificStyles.get(widget).get(key).keySet()) { - if (!mergedWidgetSpecificStyles.containsKey(widget)) { - mergedWidgetSpecificStyles.put(widget, target.widgetSpecificStyles.get(widget)); - continue; - } - - if (!mergedWidgetSpecificStyles.get(widget).containsKey(key)) { - mergedWidgetSpecificStyles.get(widget).put(key, target.widgetSpecificStyles.get(widget).get(key)); - continue; - } - - mergedWidgetSpecificStyles.get(widget).get(key).put(state, target.widgetSpecificStyles.get(widget).get(key).get(state)); - } - } - } - // Apply changes if everything went fine typeRegistry = mergedTypeRegistry; - styleClasses = mergedStyleClasses; - widgetSpecificStyles = mergedWidgetSpecificStyles; + styleClasses.moldWith(target.styleClasses); + widgetSpecificStyles.moldWith(target.widgetSpecificStyles); } public HashMap> mixClasses(LinkedHashSet classes) { final HashMap> values = new HashMap<>(); for (String clazz : classes) { - HashMap> styles = getClassStyles(clazz); + HashMap> styles = styleClasses.getPart(clazz); for (String key : styles.keySet()) values.put(key, styles.get(key)); } From 72084295a39100e6c3e1c86cb2c17df203416878 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 16:09:59 +0200 Subject: [PATCH 217/661] clean up imports --- src/main/java/net/snackbag/vera/style/StyleContainer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleContainer.java b/src/main/java/net/snackbag/vera/style/StyleContainer.java index e0be4491..377f17f7 100644 --- a/src/main/java/net/snackbag/vera/style/StyleContainer.java +++ b/src/main/java/net/snackbag/vera/style/StyleContainer.java @@ -1,8 +1,5 @@ package net.snackbag.vera.style; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import java.util.HashMap; /** From fc2945e6dc9f0a07e404ec48f4b7d012defb641a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 16:15:41 +0200 Subject: [PATCH 218/661] clarify code --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index e6fc13cf..2b967230 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -10,8 +10,10 @@ import java.util.LinkedHashSet; public class VStyleSheet { - private final StyleContainer> widgetSpecificStyles = new StyleContainer<>(); - private final StyleContainer styleClasses = new StyleContainer<>(); + private final StyleContainer> widgetSpecificStyles = new StyleContainer<>(); // like HTML #IDs + private final StyleContainer classStyles = new StyleContainer<>(); // like CSS .classes + private final StyleContainer> globalStyles = new StyleContainer<>(); // like HTML + private HashMap typeRegistry = new HashMap<>(); public VStyleSheet() { From 27729eca6c85addd1a0c0c0aa84c86866860afa4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Jun 2025 16:15:53 +0200 Subject: [PATCH 219/661] styleClasses -> classStyles --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 2b967230..f33591d9 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -103,7 +103,7 @@ public void setKey(String clazz, String key, Object value, @Nullable StyleState value = StyleValueType.convert(value, valRes); - styleClasses.put(clazz, key, state, value); + classStyles.put(clazz, key, state, value); } public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { @@ -145,7 +145,7 @@ else if (mergedTypeRegistry.get(key) != type) // Apply changes if everything went fine typeRegistry = mergedTypeRegistry; - styleClasses.moldWith(target.styleClasses); + classStyles.moldWith(target.classStyles); widgetSpecificStyles.moldWith(target.widgetSpecificStyles); } @@ -153,7 +153,7 @@ public HashMap> mixClasses(LinkedHashSet> values = new HashMap<>(); for (String clazz : classes) { - HashMap> styles = styleClasses.getPart(clazz); + HashMap> styles = classStyles.getPart(clazz); for (String key : styles.keySet()) values.put(key, styles.get(key)); } From 416dda8e8ba4f052f0733fd9e33379f4be8f9bbd Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:15:51 +0200 Subject: [PATCH 220/661] standard style --- .../net/snackbag/vera/style/standard/VStandardStyle.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java diff --git a/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java new file mode 100644 index 00000000..1e7c8352 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java @@ -0,0 +1,8 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.style.VStyleSheet; + +@FunctionalInterface +public interface VStandardStyle { + void apply(VStyleSheet sheet); +} From c633500f6e64edd6603381695947f8ffc8f99179 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:16:22 +0200 Subject: [PATCH 221/661] basic widget and label standard style --- .../style/standard/LabelStandardStyle.java | 14 +++++++++++++ .../style/standard/WidgetStandardStyle.java | 20 +++++++++++++++++++ .../java/net/snackbag/vera/widget/VLabel.java | 3 --- .../net/snackbag/vera/widget/VWidget.java | 7 ------- 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java create mode 100644 src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java diff --git a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java new file mode 100644 index 00000000..b3016518 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java @@ -0,0 +1,14 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFont; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VLabel; + +public class LabelStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VLabel.class, "background-color", VColor.transparent()); + sheet.setKey(VLabel.class, "font", VFont.create()); + } +} diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java new file mode 100644 index 00000000..3c64a890 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -0,0 +1,20 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.V4Color; +import net.snackbag.vera.core.V4Int; +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VWidget; + +public class WidgetStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VWidget.class, "overlay", VColor.transparent()); + sheet.setKey(VWidget.class, "cursor", VCursorShape.DEFAULT); + + // Border + sheet.setKey(VWidget.class, "border-color", new V4Color(VColor.black())); + sheet.setKey(VWidget.class, "border-size", new V4Int(0)); + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index d9f00b43..6eccb7d1 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -18,9 +18,6 @@ public VLabel(String text, int x, int y, int width, int height, VeraApp app) { this.padding = new V4Int(4); this.focusOnClick = false; alignment = VHAlignmentFlag.LEFT; - - setStyle("background-color", VColor.transparent()); - setStyle("font", VFont.create()); } public VLabel(String text, VeraApp app) { diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index f3d5052f..a11864ae 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -28,13 +28,6 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { super(app, x, y, width, height); this.rotation = 0; - - setStyle("overlay", VColor.transparent()); - setStyle("cursor", VCursorShape.DEFAULT); - - // Border - setStyle("border-color", new V4Color(VColor.black())); - setStyle("border-size", new V4Int(0)); } public abstract void render(); From b5b8ef2ab5251d2cd9a1d0dabb1ef27d64f82116 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:16:31 +0200 Subject: [PATCH 222/661] add registrar instance --- src/main/java/net/snackbag/vera/Vera.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index bf214f11..781747ee 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -3,10 +3,10 @@ import net.minecraft.client.MinecraftClient; import net.snackbag.mcvera.MCVeraData; import net.snackbag.mcvera.impl.MCVeraProvider; +import net.snackbag.mcvera.impl.MCVeraRegistrar; import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VWindowPositioningFlag; -import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; @@ -20,6 +20,7 @@ public class Vera { public static final MCVeraProvider provider = new MCVeraProvider(); public static final MCVeraRenderer renderer = new MCVeraRenderer(); + public static final MCVeraRegistrar registrar = new MCVeraRegistrar(); public static final String FONT_DEFAULT = provider.getDefaultFontName(); public static final String FONT_ARIAL = "minecraft:arial"; From 480f82d3d771955efe67578dbc3ca8dc73e05ba2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:16:38 +0200 Subject: [PATCH 223/661] implement registrar --- .../snackbag/mcvera/impl/MCVeraRegistrar.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java new file mode 100644 index 00000000..f59bcd0a --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java @@ -0,0 +1,21 @@ +package net.snackbag.mcvera.impl; + +import net.snackbag.vera.style.standard.VStandardStyle; +import net.snackbag.vera.style.VStyleSheet; + +import java.util.ArrayList; +import java.util.List; + +public class MCVeraRegistrar { + private final List standardStyles = new ArrayList<>(); + + public void registerStandardStyle(VStandardStyle style) { + standardStyles.add(style); + } + + public void applyStandardWidgetStyles(VStyleSheet sheet) { + for (VStandardStyle standardStyle : standardStyles) { + standardStyle.apply(sheet); + } + } +} From 5a2fd128e1f616412e611b2b2b8a0ab2c99d0f9e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:16:45 +0200 Subject: [PATCH 224/661] actually apply styles --- src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index df158a9c..b4a9f667 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -17,6 +17,7 @@ public class MCVeraProvider { public void handleAppInitialization(VeraApp app) { MCVeraData.applications.add(app); + Vera.registrar.applyStandardWidgetStyles(app.styleSheet); MinecraftClient.getInstance().send(app::init); MinecraftClient.getInstance().send(app::update); From 90f834be4a9ed8b9c98a16c39472379e311c8726 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:16:52 +0200 Subject: [PATCH 225/661] register standard styles --- src/main/java/net/snackbag/mcvera/MinecraftVera.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index 6a4262f1..cd3218b9 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -2,6 +2,9 @@ import net.fabricmc.api.ModInitializer; +import net.snackbag.vera.Vera; +import net.snackbag.vera.style.standard.LabelStandardStyle; +import net.snackbag.vera.style.standard.WidgetStandardStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,5 +15,8 @@ public class MinecraftVera implements ModInitializer { @Override public void onInitialize() { LOGGER.info("Loading Vera..."); + + Vera.registrar.registerStandardStyle(new WidgetStandardStyle()); + Vera.registrar.registerStandardStyle(new LabelStandardStyle()); } } \ No newline at end of file From 17558ac180406f699ade1c53a04ff2ec1fa07f6b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:34:44 +0200 Subject: [PATCH 226/661] add setKey for standardStyles --- .../net/snackbag/vera/style/VStyleSheet.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index f33591d9..f58ab58c 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -12,7 +12,7 @@ public class VStyleSheet { private final StyleContainer> widgetSpecificStyles = new StyleContainer<>(); // like HTML #IDs private final StyleContainer classStyles = new StyleContainer<>(); // like CSS .classes - private final StyleContainer> globalStyles = new StyleContainer<>(); // like HTML + private final StyleContainer> standardStyles = new StyleContainer<>(); // like HTML private HashMap typeRegistry = new HashMap<>(); @@ -72,6 +72,10 @@ public void setKey(String clazz, String key, Object object) { setKey(clazz, key, object, StyleState.DEFAULT); } + public void setKey(Class> clazz, String key, Object object) { + setKey(clazz, key, object, StyleState.DEFAULT); + } + public void setKey(VWidget widget, String key, Object value, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; value = potentiallyUnpackArray(value); @@ -85,7 +89,6 @@ public void setKey(VWidget widget, String key, Object value, @Nullable StyleS } else reserveType(key, valRes); value = StyleValueType.convert(value, valRes); - widgetSpecificStyles.put(widget, key, state, value); } @@ -102,10 +105,25 @@ public void setKey(String clazz, String key, Object value, @Nullable StyleState } else reserveType(key, valRes); value = StyleValueType.convert(value, valRes); - classStyles.put(clazz, key, state, value); } + public void setKey(Class> clazz, String key, Object value, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + value = potentiallyUnpackArray(value); + + StyleValueType res = getReservation(key); + StyleValueType valRes = StyleValueType.get(value, res); + + if (res != null) { + if (valRes != res) + throw new RuntimeException("Cannot set standard key %s (for class %s), because it is reserved for type %s. Received: %s".formatted(key, clazz, res, valRes)); + } else reserveType(key, valRes); + + value = StyleValueType.convert(value, valRes); + standardStyles.put(clazz, key, state, value); + } + public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { return new VColor.ColorModifier(getKey(widget, key), color -> setKey(widget, key, color)); } From 23ce6b4a037d0cf62130ded9ba0e12034d5ea694 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:37:30 +0200 Subject: [PATCH 227/661] add reserve method --- .../snackbag/vera/style/standard/LabelStandardStyle.java | 7 +++++++ .../net/snackbag/vera/style/standard/VStandardStyle.java | 2 +- .../vera/style/standard/WidgetStandardStyle.java | 9 +++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java index b3016518..8c5b1b39 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java @@ -2,6 +2,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; +import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VLabel; @@ -11,4 +12,10 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VLabel.class, "background-color", VColor.transparent()); sheet.setKey(VLabel.class, "font", VFont.create()); } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("font", StyleValueType.FONT); + } } diff --git a/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java index 1e7c8352..19a5ee2f 100644 --- a/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java @@ -2,7 +2,7 @@ import net.snackbag.vera.style.VStyleSheet; -@FunctionalInterface public interface VStandardStyle { void apply(VStyleSheet sheet); + void reserve(VStyleSheet sheet); } diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index 3c64a890..ad1f439d 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -4,6 +4,7 @@ import net.snackbag.vera.core.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VWidget; @@ -17,4 +18,12 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VWidget.class, "border-color", new V4Color(VColor.black())); sheet.setKey(VWidget.class, "border-size", new V4Int(0)); } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("overlay", StyleValueType.COLOR); + sheet.reserveType("cursor", StyleValueType.CURSOR); + sheet.reserveType("border-color", StyleValueType.V4COLOR); + sheet.reserveType("border-size", StyleValueType.V4INT); + } } From aa1534cf6b9cd0f24e56f2286713dcf3e1208dad Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:38:12 +0200 Subject: [PATCH 228/661] clear out automatically reserved --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index f58ab58c..c059989e 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -18,15 +18,8 @@ public class VStyleSheet { public VStyleSheet() { // Generic - reserveType("overlay", StyleValueType.COLOR); - reserveType("background-color", StyleValueType.COLOR); reserveType("color", StyleValueType.COLOR); reserveType("src", StyleValueType.IDENTIFIER); - reserveType("cursor", StyleValueType.CURSOR); - - // Border - reserveType("border-color", StyleValueType.V4COLOR); - reserveType("border-size", StyleValueType.V4INT); // Checkbox reserveType("src-checked", StyleValueType.IDENTIFIER); @@ -34,9 +27,6 @@ public VStyleSheet() { // Line Input reserveType("select-color", StyleValueType.COLOR); reserveType("color-cursor", StyleValueType.COLOR); - - // Font - reserveType("font", StyleValueType.FONT); } public T getKey(VWidget widget, String key) { From 7ba0fabd2a17a3c06fabc5862ca59e0a280ef4f1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:38:33 +0200 Subject: [PATCH 229/661] also call reserve --- src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java index f59bcd0a..d5c9f9f9 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java @@ -15,6 +15,7 @@ public void registerStandardStyle(VStandardStyle style) { public void applyStandardWidgetStyles(VStyleSheet sheet) { for (VStandardStyle standardStyle : standardStyles) { + standardStyle.reserve(sheet); standardStyle.apply(sheet); } } From 8edfddac1931046121c70c69e3e6c09fd1efd27a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:39:35 +0200 Subject: [PATCH 230/661] weird java stuff --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index c059989e..0916b7fc 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -62,7 +62,7 @@ public void setKey(String clazz, String key, Object object) { setKey(clazz, key, object, StyleState.DEFAULT); } - public void setKey(Class> clazz, String key, Object object) { + public void setKey(Class clazz, String key, Object object) { setKey(clazz, key, object, StyleState.DEFAULT); } @@ -98,7 +98,7 @@ public void setKey(String clazz, String key, Object value, @Nullable StyleState classStyles.put(clazz, key, state, value); } - public void setKey(Class> clazz, String key, Object value, @Nullable StyleState state) { + public void setKey(Class clazz, String key, Object value, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; value = potentiallyUnpackArray(value); From d11223417d8f174cdaf6bf413a49636e538a5543 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 16:41:29 +0200 Subject: [PATCH 231/661] add TODO to reserveTYpe --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 0916b7fc..40df773c 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -122,7 +122,7 @@ public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key) { return new VFont.FontModifier(getKey(widget, key), font -> setKey(widget, key, font)); } - public void reserveType(String key, StyleValueType type) { + public void reserveType(String key, StyleValueType type) { // TODO: make safer (aka stop if already reserved or values inside) typeRegistry.put(key, type); } From c7ee588fe5075c815601840d2cf44e05c7f3bb0b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:14:31 +0200 Subject: [PATCH 232/661] add getStandardStyle and parse standard styles as well as fixing up the getting hierarchy --- .../net/snackbag/vera/style/VStyleSheet.java | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 40df773c..190633f3 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -3,6 +3,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Array; @@ -36,6 +37,14 @@ public T getKey(VWidget widget, String key) { public T getKey(VWidget widget, String key, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; + // if widget contains key + if (widgetSpecificStyles.hasKey(widget, key)) { + // if it doesn't contain the state return with the fallback + if (!widgetSpecificStyles.hasState(widget, key, state)) return getKey(widget, key, state.fallback); + return widgetSpecificStyles.getState(widget, key, state); + } + + // if class contains key HashMap> mixed = mixClasses(widget.classes); if (mixed.containsKey(key)) { @@ -43,15 +52,40 @@ public T getKey(VWidget widget, String key, @Nullable StyleState state) { return (T) mixed.get(key).get(state); } - // if widget contains key - if (widgetSpecificStyles.hasKey(widget, key)) { - // if it doesn't contain the state return with the fallback - if (!widgetSpecificStyles.hasState(widget, key, state)) return getKey(widget, key, state.fallback); - return widgetSpecificStyles.getState(widget, key, state); + // if nothing worked, try standard keys or return null + return getStandardKey(widget.getClass(), key, state); + } + + /** + * Resolves a standard style key by traversing the class hierarchy and style states. + *

+ * Step by step:
+ * 1. If clazz is null, return null
+ * 2. If clazz is not registered in {@link VStyleSheet#standardStyles}, recurse into its superclass
+ * 3. If the specified key is not defined for this class, recurse into its superclass
+ * 4. Check if the given state is defined:
+ *     - If not, and {@link StyleState#fallback} exists, retry with the fallback state on the same class
+ *     - If fallback also fails or is null, recurse into the superclass with the original state + */ + public @Nullable T getStandardKey(@Nullable Class clazz, String key, @NotNull StyleState state) { + if (clazz == null) return null; + + // if class isn't registered, attempt super + if (!standardStyles.hasPart(clazz)) return getStandardKey(clazz.getSuperclass(), key, state); + + // if no key found, attempt super + if (!standardStyles.hasKey(clazz, key)) return getStandardKey(clazz.getSuperclass(), key, state); + + // if no state found, return same class but fallback state + if (!standardStyles.hasState(clazz, key, state)) { + // if fallback state is null, attempt superclass + if (state.fallback == null) return getStandardKey(clazz.getSuperclass(), key, state); + + // try fallback state + return getStandardKey(clazz, key, state.fallback); } - // if nothing worked, return null - return null; + return standardStyles.getState(clazz, key, state); } public void setKey(VWidget widget, String key, Object object) { From 35547e383e7bf41bfc36ff4382c6c08b10b6c99f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:15:55 +0200 Subject: [PATCH 233/661] add VRect standard style --- .../net/snackbag/mcvera/MinecraftVera.java | 2 ++ .../net/snackbag/vera/style/VStyleSheet.java | 1 - .../vera/style/standard/RectStandardStyle.java | 18 ++++++++++++++++++ .../java/net/snackbag/vera/widget/VRect.java | 1 - 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index cd3218b9..2bcf4c6d 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.style.standard.LabelStandardStyle; +import net.snackbag.vera.style.standard.RectStandardStyle; import net.snackbag.vera.style.standard.WidgetStandardStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,5 +19,6 @@ public void onInitialize() { Vera.registrar.registerStandardStyle(new WidgetStandardStyle()); Vera.registrar.registerStandardStyle(new LabelStandardStyle()); + Vera.registrar.registerStandardStyle(new RectStandardStyle()); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 190633f3..105a90e9 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -19,7 +19,6 @@ public class VStyleSheet { public VStyleSheet() { // Generic - reserveType("color", StyleValueType.COLOR); reserveType("src", StyleValueType.IDENTIFIER); // Checkbox diff --git a/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java new file mode 100644 index 00000000..321fb33d --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java @@ -0,0 +1,18 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VRect; + +public class RectStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VRect.class, "color", VColor.black()); + } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("color", StyleValueType.COLOR); + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index cdf98afe..09966c5a 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -9,7 +9,6 @@ public class VRect extends VWidget { public VRect(VColor color, VeraApp app) { super(0, 0, 20, 20, app); - setStyle("color", color); this.focusOnClick = false; } From 13608d96967ec1bb0b6a66204d66df1dede8cf8b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:20:19 +0200 Subject: [PATCH 234/661] add TabWidgetStandardStyle --- .../net/snackbag/mcvera/MinecraftVera.java | 2 ++ .../standard/TabWidgetStandardStyle.java | 19 +++++++++++++++++++ .../net/snackbag/vera/widget/VTabWidget.java | 2 -- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index 2bcf4c6d..6378414b 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -5,6 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.style.standard.LabelStandardStyle; import net.snackbag.vera.style.standard.RectStandardStyle; +import net.snackbag.vera.style.standard.TabWidgetStandardStyle; import net.snackbag.vera.style.standard.WidgetStandardStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,5 +21,6 @@ public void onInitialize() { Vera.registrar.registerStandardStyle(new WidgetStandardStyle()); Vera.registrar.registerStandardStyle(new LabelStandardStyle()); Vera.registrar.registerStandardStyle(new RectStandardStyle()); + Vera.registrar.registerStandardStyle(new TabWidgetStandardStyle()); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java new file mode 100644 index 00000000..51a7c473 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java @@ -0,0 +1,19 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VTabWidget; + +public class TabWidgetStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("cursor", StyleValueType.CURSOR); + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 593a7d64..ebc077ba 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -28,8 +28,6 @@ public VTabWidget(VeraApp app, String... tabs) { font = VFont.create(); selectedBackgroundColor = VColor.white(); defaultBackgroundColor = VColor.white().sub(40); - - setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); } @Override From 66b401c7959786352f06f744bb9a89ef03cd2be3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:23:22 +0200 Subject: [PATCH 235/661] add LineInputStandardStyle --- .../net/snackbag/mcvera/MinecraftVera.java | 6 ++--- .../standard/LineInputStandardStyle.java | 24 +++++++++++++++++++ .../net/snackbag/vera/widget/VLineInput.java | 4 ---- 3 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index 6378414b..a1b5744c 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -3,10 +3,7 @@ import net.fabricmc.api.ModInitializer; import net.snackbag.vera.Vera; -import net.snackbag.vera.style.standard.LabelStandardStyle; -import net.snackbag.vera.style.standard.RectStandardStyle; -import net.snackbag.vera.style.standard.TabWidgetStandardStyle; -import net.snackbag.vera.style.standard.WidgetStandardStyle; +import net.snackbag.vera.style.standard.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,5 +19,6 @@ public void onInitialize() { Vera.registrar.registerStandardStyle(new LabelStandardStyle()); Vera.registrar.registerStandardStyle(new RectStandardStyle()); Vera.registrar.registerStandardStyle(new TabWidgetStandardStyle()); + Vera.registrar.registerStandardStyle(new LineInputStandardStyle()); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java new file mode 100644 index 00000000..1b0360c9 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -0,0 +1,24 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VLineInput; + +public class LineInputStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VLineInput.class, "select-color", VColor.of(0, 120, 215, 0.2f)); + sheet.setKey(VLineInput.class, "background-color", VColor.transparent()); + sheet.setKey(VLineInput.class, "cursor", VCursorShape.TEXT, StyleState.HOVERED); + } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("select-color", StyleValueType.COLOR); + sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("cursor", StyleValueType.CURSOR); + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 6551e413..132a2e33 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -32,13 +32,9 @@ public VLineInput(VeraApp app) { this.placeholderFont = VFont.create().withColor(VColor.black().withOpacity(0.5f)); this.cursorPos = 0; this.textSelection = new TextSelection(); - setStyle("select-color", VColor.of(0, 120, 215, 0.2f)); this.maxChars = -1; - setStyle("background-color", VColor.transparent()); this.padding = new V4Int(4); - - setStyle("cursor", StyleState.HOVERED, VCursorShape.TEXT); } @Override From d2a5583ff729c2bde28aa44eb15e92b3f0849b9c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:25:15 +0200 Subject: [PATCH 236/661] add ImageStandardStyle --- .../java/net/snackbag/mcvera/MinecraftVera.java | 1 + .../java/net/snackbag/vera/style/VStyleSheet.java | 7 ------- .../vera/style/standard/ImageStandardStyle.java | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index a1b5744c..4e6f9145 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -20,5 +20,6 @@ public void onInitialize() { Vera.registrar.registerStandardStyle(new RectStandardStyle()); Vera.registrar.registerStandardStyle(new TabWidgetStandardStyle()); Vera.registrar.registerStandardStyle(new LineInputStandardStyle()); + Vera.registrar.registerStandardStyle(new ImageStandardStyle()); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 105a90e9..39201ad8 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -18,15 +18,8 @@ public class VStyleSheet { private HashMap typeRegistry = new HashMap<>(); public VStyleSheet() { - // Generic - reserveType("src", StyleValueType.IDENTIFIER); - // Checkbox reserveType("src-checked", StyleValueType.IDENTIFIER); - - // Line Input - reserveType("select-color", StyleValueType.COLOR); - reserveType("color-cursor", StyleValueType.COLOR); } public T getKey(VWidget widget, String key) { diff --git a/src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java new file mode 100644 index 00000000..4c9d2839 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java @@ -0,0 +1,15 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; + +public class ImageStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("src", StyleValueType.IDENTIFIER); + } +} From de81afff049e34a2bf4b64687357f40a41afb43b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:27:04 +0200 Subject: [PATCH 237/661] add DropdownStandardStyle --- .../net/snackbag/mcvera/MinecraftVera.java | 1 + .../style/standard/DropdownStandardStyle.java | 22 +++++++++++++++++++ .../net/snackbag/vera/widget/VDropdown.java | 3 --- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index 4e6f9145..a5f94ba6 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -21,5 +21,6 @@ public void onInitialize() { Vera.registrar.registerStandardStyle(new TabWidgetStandardStyle()); Vera.registrar.registerStandardStyle(new LineInputStandardStyle()); Vera.registrar.registerStandardStyle(new ImageStandardStyle()); + Vera.registrar.registerStandardStyle(new DropdownStandardStyle()); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java new file mode 100644 index 00000000..8e52bb78 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java @@ -0,0 +1,22 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VDropdown; + +public class DropdownStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VDropdown.class, "background-color", VColor.white()); + sheet.setKey(VDropdown.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("cursor", StyleValueType.CURSOR); + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 3e78802c..0566858f 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -29,11 +29,8 @@ public VDropdown(VeraApp app) { items = new ArrayList<>(); font = VFont.create(); hoverFont = VFont.create(); - setStyle("background-color", VColor.white()); itemHoverColor = VColor.white().sub(30); padding = new V4Int(5, 10); - - setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); } @Override From 5a82d37982ad2b3e6fae1773aad4d32319c50bf2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:32:28 +0200 Subject: [PATCH 238/661] add CheckBoxStandardStyle --- .../net/snackbag/mcvera/MinecraftVera.java | 2 ++ .../net/snackbag/vera/style/VStyleSheet.java | 5 ---- .../style/standard/CheckBoxStandardStyle.java | 24 +++++++++++++++++++ .../net/snackbag/vera/widget/VCheckBox.java | 3 --- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index a5f94ba6..a079224d 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -15,6 +15,7 @@ public class MinecraftVera implements ModInitializer { public void onInitialize() { LOGGER.info("Loading Vera..."); + // sorted alphabetically Vera.registrar.registerStandardStyle(new WidgetStandardStyle()); Vera.registrar.registerStandardStyle(new LabelStandardStyle()); Vera.registrar.registerStandardStyle(new RectStandardStyle()); @@ -22,5 +23,6 @@ public void onInitialize() { Vera.registrar.registerStandardStyle(new LineInputStandardStyle()); Vera.registrar.registerStandardStyle(new ImageStandardStyle()); Vera.registrar.registerStandardStyle(new DropdownStandardStyle()); + Vera.registrar.registerStandardStyle(new CheckBoxStandardStyle()); } } \ No newline at end of file diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 39201ad8..bf35dab1 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -17,11 +17,6 @@ public class VStyleSheet { private HashMap typeRegistry = new HashMap<>(); - public VStyleSheet() { - // Checkbox - reserveType("src-checked", StyleValueType.IDENTIFIER); - } - public T getKey(VWidget widget, String key) { return getKey(widget, key, StyleState.DEFAULT); } diff --git a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java new file mode 100644 index 00000000..60e63300 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java @@ -0,0 +1,24 @@ +package net.snackbag.vera.style.standard; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.widget.VCheckBox; + +public class CheckBoxStandardStyle implements VStandardStyle { + @Override + public void apply(VStyleSheet sheet) { + sheet.setKey(VCheckBox.class, "overlay", VColor.transparent()); + sheet.setKey(VCheckBox.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("overlay", StyleValueType.COLOR); + sheet.reserveType("cursor", StyleValueType.CURSOR); + sheet.reserveType("src", StyleValueType.IDENTIFIER); + sheet.reserveType("src-checked", StyleValueType.IDENTIFIER); + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 7e602648..a79327c4 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -31,9 +31,6 @@ public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTextu setStyle("src", defaultTexture); setStyle("src-checked", checkedTexture); - setStyle("overlay", VColor.transparent()); - - setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); } @Override From f77618ad0d119af9245b401a81dbb4aad1cb306a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:33:17 +0200 Subject: [PATCH 239/661] reorder --- src/main/java/net/snackbag/mcvera/MinecraftVera.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index a079224d..9ba92ced 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -15,14 +15,15 @@ public class MinecraftVera implements ModInitializer { public void onInitialize() { LOGGER.info("Loading Vera..."); - // sorted alphabetically + // sorted by "complicatedness" & importance Vera.registrar.registerStandardStyle(new WidgetStandardStyle()); - Vera.registrar.registerStandardStyle(new LabelStandardStyle()); + Vera.registrar.registerStandardStyle(new RectStandardStyle()); - Vera.registrar.registerStandardStyle(new TabWidgetStandardStyle()); - Vera.registrar.registerStandardStyle(new LineInputStandardStyle()); Vera.registrar.registerStandardStyle(new ImageStandardStyle()); - Vera.registrar.registerStandardStyle(new DropdownStandardStyle()); Vera.registrar.registerStandardStyle(new CheckBoxStandardStyle()); + Vera.registrar.registerStandardStyle(new LabelStandardStyle()); + Vera.registrar.registerStandardStyle(new DropdownStandardStyle()); + Vera.registrar.registerStandardStyle(new TabWidgetStandardStyle()); + Vera.registrar.registerStandardStyle(new LineInputStandardStyle()); } } \ No newline at end of file From 8af47fb042735c1d9d862038b58546136db3c81b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:33:41 +0200 Subject: [PATCH 240/661] add log for registering --- src/main/java/net/snackbag/mcvera/MinecraftVera.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/mcvera/MinecraftVera.java b/src/main/java/net/snackbag/mcvera/MinecraftVera.java index 9ba92ced..66caf1e1 100644 --- a/src/main/java/net/snackbag/mcvera/MinecraftVera.java +++ b/src/main/java/net/snackbag/mcvera/MinecraftVera.java @@ -15,6 +15,7 @@ public class MinecraftVera implements ModInitializer { public void onInitialize() { LOGGER.info("Loading Vera..."); + LOGGER.info("Registering standard styles"); // sorted by "complicatedness" & importance Vera.registrar.registerStandardStyle(new WidgetStandardStyle()); From 9cbe4bc542574c817e9064aada7fd577a02c0767 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:34:15 +0200 Subject: [PATCH 241/661] make reserved not required --- .../java/net/snackbag/vera/style/standard/VStandardStyle.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java index 19a5ee2f..2e9d8f2f 100644 --- a/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/VStandardStyle.java @@ -4,5 +4,5 @@ public interface VStandardStyle { void apply(VStyleSheet sheet); - void reserve(VStyleSheet sheet); + default void reserve(VStyleSheet sheet) {} } From a95d2c79f4ea37e5f346e077c32e221e91b0e6b0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:35:40 +0200 Subject: [PATCH 242/661] remove unneeded reserve method --- .../snackbag/vera/style/standard/TabWidgetStandardStyle.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java index 51a7c473..4cb3b0a8 100644 --- a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java @@ -11,9 +11,4 @@ public class TabWidgetStandardStyle implements VStandardStyle { public void apply(VStyleSheet sheet) { sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } - - @Override - public void reserve(VStyleSheet sheet) { - sheet.reserveType("cursor", StyleValueType.CURSOR); - } } From 12069762f5b1ca144ea1fda003b00576444a170d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:35:45 +0200 Subject: [PATCH 243/661] color -> background-color --- .../net/snackbag/vera/style/standard/RectStandardStyle.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java index 321fb33d..b3c683a5 100644 --- a/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java @@ -8,11 +8,11 @@ public class RectStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { - sheet.setKey(VRect.class, "color", VColor.black()); + sheet.setKey(VRect.class, "background-color", VColor.black()); } @Override public void reserve(VStyleSheet sheet) { - sheet.reserveType("color", StyleValueType.COLOR); + sheet.reserveType("background-color", StyleValueType.COLOR); } } From 5d7da03a0de303ebe375eb2112ca7150d70e11e7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:35:57 +0200 Subject: [PATCH 244/661] no longer reserve cursor --- .../net/snackbag/vera/style/standard/DropdownStandardStyle.java | 1 - .../net/snackbag/vera/style/standard/LineInputStandardStyle.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java index 8e52bb78..a39efd03 100644 --- a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java @@ -17,6 +17,5 @@ public void apply(VStyleSheet sheet) { @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("background-color", StyleValueType.COLOR); - sheet.reserveType("cursor", StyleValueType.CURSOR); } } diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java index 1b0360c9..097f5431 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -19,6 +19,5 @@ public void apply(VStyleSheet sheet) { public void reserve(VStyleSheet sheet) { sheet.reserveType("select-color", StyleValueType.COLOR); sheet.reserveType("background-color", StyleValueType.COLOR); - sheet.reserveType("cursor", StyleValueType.CURSOR); } } From 2a9e03ae9ee4676c27605c549770ab756ddca965 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:36:01 +0200 Subject: [PATCH 245/661] no longer reserve cursor and overlay --- .../net/snackbag/vera/style/standard/CheckBoxStandardStyle.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java index 60e63300..6a93fec6 100644 --- a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java @@ -16,8 +16,6 @@ public void apply(VStyleSheet sheet) { @Override public void reserve(VStyleSheet sheet) { - sheet.reserveType("overlay", StyleValueType.COLOR); - sheet.reserveType("cursor", StyleValueType.CURSOR); sheet.reserveType("src", StyleValueType.IDENTIFIER); sheet.reserveType("src-checked", StyleValueType.IDENTIFIER); } From d9cd5e03ab07a5186f86885c1ded59b94f9c02fb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 17:36:24 +0200 Subject: [PATCH 246/661] add TODO --- .../net/snackbag/vera/style/standard/WidgetStandardStyle.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index ad1f439d..bc749833 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -25,5 +25,7 @@ public void reserve(VStyleSheet sheet) { sheet.reserveType("cursor", StyleValueType.CURSOR); sheet.reserveType("border-color", StyleValueType.V4COLOR); sheet.reserveType("border-size", StyleValueType.V4INT); + + // TODO: add background-color } } From d849567e0d8b8e04c64e75c79022f1cfd3c837e3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 21:45:39 +0200 Subject: [PATCH 247/661] no more standard overlay --- .../net/snackbag/vera/style/standard/CheckBoxStandardStyle.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java index 6a93fec6..a37862b5 100644 --- a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java @@ -10,7 +10,6 @@ public class CheckBoxStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { - sheet.setKey(VCheckBox.class, "overlay", VColor.transparent()); sheet.setKey(VCheckBox.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } From 30b695725b8baf283d4282ae127d63a9db80d840 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 21:45:44 +0200 Subject: [PATCH 248/661] import --- .../net/snackbag/vera/style/standard/CheckBoxStandardStyle.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java index a37862b5..f9daabad 100644 --- a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java @@ -1,6 +1,5 @@ package net.snackbag.vera.style.standard; -import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.StyleValueType; From 5909ade0734d943f15289804d0ce5e2d4599af1d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 21:46:03 +0200 Subject: [PATCH 249/661] add clear test option --- .../net/snackbag/mcvera/InternalCommands.java | 37 ++++++++++++------- .../mcvera/test/LayoutTestApplication.java | 2 +- .../mcvera/test/StyleTestApplication.java | 2 +- .../snackbag/mcvera/test/TestApplication.java | 2 +- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/InternalCommands.java b/src/main/java/net/snackbag/mcvera/InternalCommands.java index 0322dcb1..ac16ce0d 100644 --- a/src/main/java/net/snackbag/mcvera/InternalCommands.java +++ b/src/main/java/net/snackbag/mcvera/InternalCommands.java @@ -17,20 +17,29 @@ public static void register( if (!FabricLoader.getInstance().isDevelopmentEnvironment()) return; dispatcher.register( - ClientCommandManager.literal("vera").then(ClientCommandManager.literal("test") - .then(ClientCommandManager.literal("generic").executes((ctx) -> { - TestApplication.INSTANCE.show(); - return 1; - })) - .then(ClientCommandManager.literal("styles").executes((ctx) -> { - StyleTestApplication.INSTANCE.show(); - return 1; - })) - .then(ClientCommandManager.literal("layout").executes((ctx) -> { - LayoutTestApplication.INSTANCE.show(); - return 1; - })) - ) + ClientCommandManager.literal("vera") + .then(ClientCommandManager.literal("test") + .then(ClientCommandManager.literal("generic").executes((ctx) -> { + TestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("styles").executes((ctx) -> { + StyleTestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("layout").executes((ctx) -> { + LayoutTestApplication.INSTANCE.show(); + return 1; + })) + ) + .then(ClientCommandManager.literal("clear-tests") + .executes((ctx) -> { + TestApplication.INSTANCE = new TestApplication(); + StyleTestApplication.INSTANCE = new StyleTestApplication(); + LayoutTestApplication.INSTANCE = new LayoutTestApplication(); + return 1; + }) + ) ); } } diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java index 36ae1adb..a1fafd99 100644 --- a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java @@ -8,7 +8,7 @@ import net.snackbag.vera.widget.VLabel; public class LayoutTestApplication extends VeraApp { - public static final LayoutTestApplication INSTANCE = new LayoutTestApplication(); + public static LayoutTestApplication INSTANCE = new LayoutTestApplication(); @Override public void init() { diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index c6f155e6..ea0585df 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -11,7 +11,7 @@ import net.snackbag.vera.widget.VRect; public class StyleTestApplication extends VeraApp { - public static final StyleTestApplication INSTANCE = new StyleTestApplication(); + public static StyleTestApplication INSTANCE = new StyleTestApplication(); @Override public void init() { diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 89102b70..d5d7ce5e 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -13,7 +13,7 @@ import java.nio.file.Path; public class TestApplication extends VeraApp { - public static final TestApplication INSTANCE = new TestApplication(); + public static TestApplication INSTANCE = new TestApplication(); public TestApplication() { super(); From 0e4372bac93db856f3e82cf30c7e220af9f18685 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 21:46:19 +0200 Subject: [PATCH 250/661] rect color -> background-color --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 2 +- src/main/java/net/snackbag/vera/widget/VRect.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index ea0585df..869885e6 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -24,7 +24,7 @@ public void init() { .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); - testRect.setStyle("color", StyleState.HOVERED, VColor.white()); + testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 09966c5a..7d284b0a 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -16,6 +16,6 @@ public VRect(VColor color, VeraApp app) { public void render() { StyleState state = createStyleState(); - Vera.renderer.drawRect(app, getX(), getY(), width, height, rotation, getStyle("color", state)); + Vera.renderer.drawRect(app, getX(), getY(), width, height, rotation, getStyle("background-color", state)); } } From 8fdef1352a02d587ccfd47fa19ba3ba4d319649b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 21:46:54 +0200 Subject: [PATCH 251/661] fix crash --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index bf35dab1..05108209 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -26,9 +26,11 @@ public T getKey(VWidget widget, String key, @Nullable StyleState state) { // if widget contains key if (widgetSpecificStyles.hasKey(widget, key)) { - // if it doesn't contain the state return with the fallback - if (!widgetSpecificStyles.hasState(widget, key, state)) return getKey(widget, key, state.fallback); - return widgetSpecificStyles.getState(widget, key, state); + // if widget has state, return + if (widgetSpecificStyles.hasState(widget, key, state)) return widgetSpecificStyles.getState(widget, key, state); + + // if widget state has fallback, attempt + if (state.fallback != null) return getKey(widget, key, state.fallback); } // if class contains key From 52e5834714528ea7b285796a8b6723f86072dbe9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 21:47:00 +0200 Subject: [PATCH 252/661] mold standard registry --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 05108209..11ad1932 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -176,6 +176,7 @@ else if (mergedTypeRegistry.get(key) != type) // Apply changes if everything went fine typeRegistry = mergedTypeRegistry; + standardStyles.moldWith(target.standardStyles); classStyles.moldWith(target.classStyles); widgetSpecificStyles.moldWith(target.widgetSpecificStyles); } From fa9a5b0ed18e2bdc19b657d9829a0ed56ea2e120 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:07:55 +0200 Subject: [PATCH 253/661] more elegant solution done --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 8 ++++++++ src/main/java/net/snackbag/vera/widget/VLabel.java | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 11ad1932..6606a996 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -145,6 +145,14 @@ public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key) { return new VFont.FontModifier(getKey(widget, key), font -> setKey(widget, key, font)); } + public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key) { + // this is cursed + return new VColor.ColorModifier( + ((VFont) getKey(widget, key)).getColor(), + color -> modifyKeyAsFont(widget, key).color(color) + ); + } + public void reserveType(String key, StyleValueType type) { // TODO: make safer (aka stop if already reserved or values inside) typeRegistry.put(key, type); } diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 6eccb7d1..7411c3df 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -88,8 +88,7 @@ public VFont.FontModifier modifyFont(String key) { } public VColor.ColorModifier modifyFontColor(String key) { - // TODO: More elegant solution - return new VColor.ColorModifier(((VFont) app.styleSheet.getKey(this, key)).getColor(), color -> app.styleSheet.modifyKeyAsFont(this, key).color(color)); + return app.styleSheet.modifyKeyAsFontColor(this, key); } @Override From 4baa18bbb2f16c34f275eb8a3a40579401db101b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:08:04 +0200 Subject: [PATCH 254/661] make tab widget font be styled --- .../standard/TabWidgetStandardStyle.java | 7 +++++ .../net/snackbag/vera/widget/VTabWidget.java | 26 +++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java index 4cb3b0a8..9a014c21 100644 --- a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java @@ -1,6 +1,7 @@ package net.snackbag.vera.style.standard; import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.core.VFont; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; @@ -9,6 +10,12 @@ public class TabWidgetStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { + sheet.setKey(VTabWidget.class, "font", VFont.create()); sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } + + @Override + public void reserve(VStyleSheet sheet) { + sheet.reserveType("font", StyleValueType.FONT); + } } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index ebc077ba..21c5d36c 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -2,7 +2,6 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VColor; -import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.StyleState; @@ -12,7 +11,6 @@ import java.util.function.Consumer; public class VTabWidget extends VWidget { - private VFont font; private VColor selectedBackgroundColor; private VColor defaultBackgroundColor; private int itemSpacingLeft = 4; @@ -25,13 +23,15 @@ public class VTabWidget extends VWidget { public VTabWidget(VeraApp app, String... tabs) { super(0, 0, 100, 16, app); - font = VFont.create(); selectedBackgroundColor = VColor.white(); defaultBackgroundColor = VColor.white().sub(40); } @Override public void render() { + StyleState state = createStyleState(); + VFont font = getStyle("font", state); + int marginX = 0; int i = -1; @@ -112,7 +112,7 @@ public int getHoveredTabIndex(int mouseX) { int index = 0; for (String tabName : tabs.keySet()) { - int textWidth = Vera.provider.getTextWidth(tabName, font); + int textWidth = Vera.provider.getTextWidth(tabName, getStyle("font", createStyleState())); int totalTabWidth = itemSpacingLeft + textWidth + itemSpacingRight; if (relativeX >= currentX && relativeX < currentX + totalTabWidth) { @@ -194,15 +194,17 @@ public void addWidget(String tab, List> widgets) { @Override public int getHitboxHeight() { - return font.getSize() / 2 + 4; + return ((VFont) getStyle("font", createStyleState())).getSize() / 2 + 4; } @Override public int getHitboxWidth() { + StyleState state = createStyleState(); + int currentX = 0; for (String tabName : tabs.keySet()) { - int textWidth = Vera.provider.getTextWidth(tabName, font); + int textWidth = Vera.provider.getTextWidth(tabName, getStyle("font", state)); int totalTabWidth = itemSpacingLeft + textWidth + itemSpacingRight; currentX += totalTabWidth; @@ -223,20 +225,12 @@ public void setActiveTab(@Nullable Integer activeTab) { this.activeTab = activeTab; } - public VFont getFont() { - return font; - } - - public void setFont(VFont font) { - this.font = font; - } - public VFont.FontModifier modifyFont() { - return new VFont.FontModifier(font, this::setFont); + return app.styleSheet.modifyKeyAsFont(this, "font"); } public VColor.ColorModifier modifyFontColor() { - return new VColor.ColorModifier(font.getColor(), (color) -> setFont(getFont().withColor(color))); + return app.styleSheet.modifyKeyAsFontColor(this, "font"); } public VColor getSelectedBackgroundColor() { From 88d667efe22df400041d39af3b5428b4e079253c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:12:32 +0200 Subject: [PATCH 255/661] remove redundant argument --- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 21c5d36c..240b6d5d 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -20,7 +20,7 @@ public class VTabWidget extends VWidget { private @Nullable Integer activeTab = null; private @Nullable Integer hoveredTab = null; - public VTabWidget(VeraApp app, String... tabs) { + public VTabWidget(VeraApp app) { super(0, 0, 100, 16, app); selectedBackgroundColor = VColor.white(); From ae837dbfe07c0e14cb22cafe9685fa289eff09de Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:15:11 +0200 Subject: [PATCH 256/661] background-color and background-color-selected are now correctly implemented --- .../standard/TabWidgetStandardStyle.java | 5 +++ .../net/snackbag/vera/widget/VTabWidget.java | 32 ++++--------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java index 9a014c21..c90fc0fe 100644 --- a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java @@ -1,5 +1,6 @@ package net.snackbag.vera.style.standard; +import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; import net.snackbag.vera.style.StyleState; @@ -10,6 +11,8 @@ public class TabWidgetStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { + sheet.setKey(VTabWidget.class, "background-color", VColor.white()); + sheet.setKey(VTabWidget.class, "background-color-selected", VColor.white().sub(40)); sheet.setKey(VTabWidget.class, "font", VFont.create()); sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); } @@ -17,5 +20,7 @@ public void apply(VStyleSheet sheet) { @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("font", StyleValueType.FONT); + sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("background-color-selected", StyleValueType.COLOR); } } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 240b6d5d..c2e2b3ad 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -11,8 +11,6 @@ import java.util.function.Consumer; public class VTabWidget extends VWidget { - private VColor selectedBackgroundColor; - private VColor defaultBackgroundColor; private int itemSpacingLeft = 4; private int itemSpacingRight = 4; @@ -22,15 +20,15 @@ public class VTabWidget extends VWidget { public VTabWidget(VeraApp app) { super(0, 0, 100, 16, app); - - selectedBackgroundColor = VColor.white(); - defaultBackgroundColor = VColor.white().sub(40); } @Override public void render() { StyleState state = createStyleState(); + VFont font = getStyle("font", state); + VColor defaultBackgroundColor = getStyle("background-color", state); + VColor selectedBackgroundColor = getStyle("background-color-selected", state); int marginX = 0; int i = -1; @@ -233,28 +231,12 @@ public VColor.ColorModifier modifyFontColor() { return app.styleSheet.modifyKeyAsFontColor(this, "font"); } - public VColor getSelectedBackgroundColor() { - return selectedBackgroundColor; - } - - public void setSelectedBackgroundColor(VColor backgroundColor) { - this.selectedBackgroundColor = backgroundColor; - } - - public VColor.ColorModifier modifySelectedBackgroundColor() { - return new VColor.ColorModifier(selectedBackgroundColor, this::setSelectedBackgroundColor); - } - - public VColor getDefaultBackgroundColor() { - return defaultBackgroundColor; - } - - public void setDefaultBackgroundColor(VColor defaultBackgroundColor) { - this.defaultBackgroundColor = defaultBackgroundColor; + public VColor.ColorModifier modifyBackgroundColorSelected() { + return app.styleSheet.modifyKeyAsColor(this, "background-color-selected"); } - public VColor.ColorModifier modifyDefaultBackgroundColor() { - return new VColor.ColorModifier(defaultBackgroundColor, this::setDefaultBackgroundColor); + public VColor.ColorModifier modifyBackgroundColor() { + return app.styleSheet.modifyKeyAsColor(this, "background-color"); } public int getItemSpacingLeft() { From e5d96aa50ed1af141f31198462950ef7528804f9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:18:51 +0200 Subject: [PATCH 257/661] item-spacing-left and item-spacing-right are now correct styles --- .../standard/TabWidgetStandardStyle.java | 6 ++++ .../net/snackbag/vera/widget/VTabWidget.java | 35 ++++++++----------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java index c90fc0fe..75034653 100644 --- a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java @@ -15,6 +15,9 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VTabWidget.class, "background-color-selected", VColor.white().sub(40)); sheet.setKey(VTabWidget.class, "font", VFont.create()); sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + + sheet.setKey(VTabWidget.class, "item-spacing-left", 4); + sheet.setKey(VTabWidget.class, "item-spacing-right", 4); } @Override @@ -22,5 +25,8 @@ public void reserve(VStyleSheet sheet) { sheet.reserveType("font", StyleValueType.FONT); sheet.reserveType("background-color", StyleValueType.COLOR); sheet.reserveType("background-color-selected", StyleValueType.COLOR); + + sheet.reserveType("item-spacing-left", StyleValueType.INT); + sheet.reserveType("item-spacing-right", StyleValueType.INT); } } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index c2e2b3ad..e8c4929e 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -11,9 +11,6 @@ import java.util.function.Consumer; public class VTabWidget extends VWidget { - private int itemSpacingLeft = 4; - private int itemSpacingRight = 4; - private final LinkedHashMap>> tabs = new LinkedHashMap<>(); private @Nullable Integer activeTab = null; private @Nullable Integer hoveredTab = null; @@ -29,6 +26,8 @@ public void render() { VFont font = getStyle("font", state); VColor defaultBackgroundColor = getStyle("background-color", state); VColor selectedBackgroundColor = getStyle("background-color-selected", state); + int itemSpacingLeft = getStyle("item-spacing-left", state); + int itemSpacingRight = getStyle("item-spacing-right", state); int marginX = 0; int i = -1; @@ -105,12 +104,18 @@ public boolean isValidTabIndex(@Nullable Integer index) { } public int getHoveredTabIndex(int mouseX) { + StyleState state = createStyleState(); + + VFont font = getStyle("font", state); + int itemSpacingLeft = getStyle("item-spacing-left", state); + int itemSpacingRight = getStyle("item-spacing-right", state); + int relativeX = mouseX - getHitboxX(); int currentX = 0; int index = 0; for (String tabName : tabs.keySet()) { - int textWidth = Vera.provider.getTextWidth(tabName, getStyle("font", createStyleState())); + int textWidth = Vera.provider.getTextWidth(tabName, font); int totalTabWidth = itemSpacingLeft + textWidth + itemSpacingRight; if (relativeX >= currentX && relativeX < currentX + totalTabWidth) { @@ -199,10 +204,14 @@ public int getHitboxHeight() { public int getHitboxWidth() { StyleState state = createStyleState(); + VFont font = getStyle("font", state); + int itemSpacingLeft = getStyle("item-spacing-left", state); + int itemSpacingRight = getStyle("item-spacing-right", state); + int currentX = 0; for (String tabName : tabs.keySet()) { - int textWidth = Vera.provider.getTextWidth(tabName, getStyle("font", state)); + int textWidth = Vera.provider.getTextWidth(tabName, font); int totalTabWidth = itemSpacingLeft + textWidth + itemSpacingRight; currentX += totalTabWidth; @@ -238,20 +247,4 @@ public VColor.ColorModifier modifyBackgroundColorSelected() { public VColor.ColorModifier modifyBackgroundColor() { return app.styleSheet.modifyKeyAsColor(this, "background-color"); } - - public int getItemSpacingLeft() { - return itemSpacingLeft; - } - - public void setItemSpacingLeft(int itemSpacingLeft) { - this.itemSpacingLeft = itemSpacingLeft; - } - - public int getItemSpacingRight() { - return itemSpacingRight; - } - - public void setItemSpacingRight(int itemSpacingRight) { - this.itemSpacingRight = itemSpacingRight; - } } From b8ec545425ce58cc72b8c168fae38fa08f03b2b8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:38:29 +0200 Subject: [PATCH 258/661] make font and placeholder-font be proper styles --- .../standard/LineInputStandardStyle.java | 5 ++ .../net/snackbag/vera/widget/VLineInput.java | 46 +++++++++---------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java index 097f5431..78430147 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -2,6 +2,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.core.VFont; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; @@ -13,11 +14,15 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VLineInput.class, "select-color", VColor.of(0, 120, 215, 0.2f)); sheet.setKey(VLineInput.class, "background-color", VColor.transparent()); sheet.setKey(VLineInput.class, "cursor", VCursorShape.TEXT, StyleState.HOVERED); + sheet.setKey(VLineInput.class, "font", VFont.create()); + sheet.setKey(VLineInput.class, "placeholder-font", VFont.create().withColor(VColor.black().withOpacity(0.5f))); } @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("select-color", StyleValueType.COLOR); sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("font", StyleValueType.FONT); + sheet.reserveType("placeholder-font", StyleValueType.FONT); } } diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 132a2e33..72c0175b 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -14,8 +14,6 @@ public class VLineInput extends VWidget implements VPaddingWidget { private String text; private String placeholderText; - private VFont font; - private VFont placeholderFont; private int cursorPos; private TextSelection textSelection; @@ -28,8 +26,6 @@ public VLineInput(VeraApp app) { this.text = ""; this.placeholderText = ""; - this.font = VFont.create(); - this.placeholderFont = VFont.create().withColor(VColor.black().withOpacity(0.5f)); this.cursorPos = 0; this.textSelection = new TextSelection(); this.maxChars = -1; @@ -41,6 +37,8 @@ public VLineInput(VeraApp app) { public void render() { StyleState state = createStyleState(); + VFont font = getStyle("font", state); + VFont placeholderFont = getStyle("placeholder-font", state); VColor backgroundColor = getStyle("background-color", state); VColor textSelectionColor = getStyle("select-color", state); @@ -96,6 +94,9 @@ public void render() { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); + StyleState state = createStyleState(); + VFont font = getStyle("font", state); + int x = getX(); if (event.equals("left-click")) { @@ -106,36 +107,20 @@ public void handleBuiltinEvent(String event, Object... args) { } } - public VFont getFont() { - return font; - } - - public void setFont(VFont font) { - this.font = font; - } - - public VFont getPlaceholderFont() { - return placeholderFont; - } - - public void setPlaceholderFont(VFont placeholderFont) { - this.placeholderFont = placeholderFont; - } - public VFont.FontModifier modifyFont() { - return new VFont.FontModifier(font, this::setFont); + return app.styleSheet.modifyKeyAsFont(this, "font"); } public VColor.ColorModifier modifyFontColor() { - return new VColor.ColorModifier(font.getColor(), (color) -> setFont(font.withColor(color))); + return app.styleSheet.modifyKeyAsFontColor(this, "font"); } public VFont.FontModifier modifyPlaceholderFont() { - return new VFont.FontModifier(font, this::setPlaceholderFont); + return app.styleSheet.modifyKeyAsFont(this, "placeholder-font"); } public VColor.ColorModifier modifyPlaceholderFontColor() { - return new VColor.ColorModifier(font.getColor(), (color) -> setFont(font.withColor(color))); + return app.styleSheet.modifyKeyAsFontColor(this, "placeholder-font"); } public String getText() { @@ -413,7 +398,10 @@ public void setCursorPos(int cursorPos) { } public VColor getCursorColorSafe() { - VColor style = getStyleOrDefault("cursor-color", null); + StyleState state = createStyleState(); + + VColor style = getStyleOrDefault("cursor-color", null, state); + VFont font = getStyle("font", state); return style == null ? font.getColor() : style; } @@ -430,11 +418,19 @@ public void setPadding(V4Int padding) { @Override public int getHitboxWidth() { + StyleState state = createStyleState(); + + VFont font = getStyle("font", state); + return Math.max(width, Vera.provider.getTextWidth(text, font)) + padding.get3() + padding.get4(); } @Override public int getHitboxHeight() { + StyleState state = createStyleState(); + + VFont font = getStyle("font", state); + return Vera.provider.getTextHeight(text, font) + padding.get1() + padding.get2(); } From c976d72a033fae8a6455722a1ac026878fc5a689 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:42:40 +0200 Subject: [PATCH 259/661] mark padding as deprecated --- src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java | 1 + .../net/snackbag/vera/style/standard/WidgetStandardStyle.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java index 5a71a1e7..3a749622 100644 --- a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java +++ b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java @@ -2,6 +2,7 @@ import net.snackbag.vera.core.V4Int; +@Deprecated(forRemoval = true, since = "1.10") public interface VPaddingWidget { V4Int getPadding(); diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index bc749833..f430e3a8 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -27,5 +27,6 @@ public void reserve(VStyleSheet sheet) { sheet.reserveType("border-size", StyleValueType.V4INT); // TODO: add background-color + // TODO: add padding } } From 6eb47fc6cf85283e56565e759049051cde0ad6d5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:42:50 +0200 Subject: [PATCH 260/661] line input now uses padding correctly --- .../standard/LineInputStandardStyle.java | 3 +++ .../net/snackbag/vera/widget/VLineInput.java | 21 +++++-------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java index 78430147..9b622d4b 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -1,5 +1,6 @@ package net.snackbag.vera.style.standard; +import net.snackbag.vera.core.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; @@ -16,6 +17,7 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VLineInput.class, "cursor", VCursorShape.TEXT, StyleState.HOVERED); sheet.setKey(VLineInput.class, "font", VFont.create()); sheet.setKey(VLineInput.class, "placeholder-font", VFont.create().withColor(VColor.black().withOpacity(0.5f))); + sheet.setKey(VLineInput.class, "padding", new V4Int(4)); } @Override @@ -24,5 +26,6 @@ public void reserve(VStyleSheet sheet) { sheet.reserveType("background-color", StyleValueType.COLOR); sheet.reserveType("font", StyleValueType.FONT); sheet.reserveType("placeholder-font", StyleValueType.FONT); + sheet.reserveType("padding", StyleValueType.V4INT); } } diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 72c0175b..766c37be 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -5,13 +5,12 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.VCharLimitedEvent; -import net.snackbag.vera.modifier.VPaddingWidget; import net.snackbag.vera.style.StyleState; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -public class VLineInput extends VWidget implements VPaddingWidget { +public class VLineInput extends VWidget { private String text; private String placeholderText; @@ -19,8 +18,6 @@ public class VLineInput extends VWidget implements VPaddingWidget { private TextSelection textSelection; private int maxChars; - private V4Int padding; - public VLineInput(VeraApp app) { super(0, 0, 100, 20, app); @@ -29,8 +26,6 @@ public VLineInput(VeraApp app) { this.cursorPos = 0; this.textSelection = new TextSelection(); this.maxChars = -1; - - this.padding = new V4Int(4); } @Override @@ -406,21 +401,12 @@ public VColor getCursorColorSafe() { return style == null ? font.getColor() : style; } - @Override - public V4Int getPadding() { - return padding; - } - - @Override - public void setPadding(V4Int padding) { - this.padding = padding; - } - @Override public int getHitboxWidth() { StyleState state = createStyleState(); VFont font = getStyle("font", state); + V4Int padding = getStyle("padding", state); return Math.max(width, Vera.provider.getTextWidth(text, font)) + padding.get3() + padding.get4(); } @@ -430,6 +416,7 @@ public int getHitboxHeight() { StyleState state = createStyleState(); VFont font = getStyle("font", state); + V4Int padding = getStyle("padding", state); return Vera.provider.getTextHeight(text, font) + padding.get1() + padding.get2(); } @@ -437,11 +424,13 @@ public int getHitboxHeight() { @Override public int getHitboxX() { + V4Int padding = getStyle("padding", createStyleState()); return getX() - padding.get4(); } @Override public int getHitboxY() { + V4Int padding = getStyle("padding", createStyleState()); return getY() - padding.get1(); } From 6beb970879d58e63b6f70224f85785dfe132e178 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:45:13 +0200 Subject: [PATCH 261/661] label now uses padding correctly --- .../style/standard/LabelStandardStyle.java | 3 +++ .../java/net/snackbag/vera/widget/VLabel.java | 21 +++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java index 8c5b1b39..070a4596 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java @@ -1,5 +1,6 @@ package net.snackbag.vera.style.standard; +import net.snackbag.vera.core.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.style.StyleValueType; @@ -11,11 +12,13 @@ public class LabelStandardStyle implements VStandardStyle { public void apply(VStyleSheet sheet) { sheet.setKey(VLabel.class, "background-color", VColor.transparent()); sheet.setKey(VLabel.class, "font", VFont.create()); + sheet.setKey(VLabel.class, "padding", new V4Int(4)); } @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("background-color", StyleValueType.COLOR); sheet.reserveType("font", StyleValueType.FONT); + sheet.reserveType("padding", StyleValueType.V4INT); } } diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 7411c3df..28ae9986 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -3,19 +3,16 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.flag.VHAlignmentFlag; -import net.snackbag.vera.modifier.VPaddingWidget; import net.snackbag.vera.style.StyleState; -public class VLabel extends VWidget implements VPaddingWidget { +public class VLabel extends VWidget { private String text; - private V4Int padding; private VHAlignmentFlag alignment; public VLabel(String text, int x, int y, int width, int height, VeraApp app) { super(x, y, width, height, app); this.text = text; - this.padding = new V4Int(4); this.focusOnClick = false; alignment = VHAlignmentFlag.LEFT; } @@ -34,33 +31,27 @@ public void setText(String text) { this.text = text; } - @Override - public V4Int getPadding() { - return padding; - } - - @Override - public void setPadding(V4Int padding) { - this.padding = padding; - } - @Override public int getHitboxWidth() { + V4Int padding = getStyle("padding", createStyleState()); return width + padding.get3() + padding.get4(); } @Override public int getHitboxHeight() { + V4Int padding = getStyle("padding", createStyleState()); return height + padding.get1() + padding.get2(); } @Override public int getHitboxX() { + V4Int padding = getStyle("padding", createStyleState()); return getX() - padding.get4(); } @Override public int getHitboxY() { + V4Int padding = getStyle("padding", createStyleState()); return getY() - padding.get1(); } @@ -94,8 +85,10 @@ public VColor.ColorModifier modifyFontColor(String key) { @Override public void render() { StyleState state = createStyleState(); + VFont font = getStyle("font", state); VColor backgroundColor = getStyle("background-color", state); + V4Int padding = getStyle("padding", state); int x = getX(); int y = getY(); From fb27fca5e883d496492abc9173e7f164c44fd0d3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:48:38 +0200 Subject: [PATCH 262/661] VDropdown now correctly implements fonts --- .../style/standard/DropdownStandardStyle.java | 3 ++ .../net/snackbag/vera/widget/VDropdown.java | 39 ++++--------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java index a39efd03..ac6b9d26 100644 --- a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java @@ -2,6 +2,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.core.VFont; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; @@ -12,10 +13,12 @@ public class DropdownStandardStyle implements VStandardStyle { public void apply(VStyleSheet sheet) { sheet.setKey(VDropdown.class, "background-color", VColor.white()); sheet.setKey(VDropdown.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + sheet.setKey(VDropdown.class, "font", VFont.create()); } @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("font", StyleValueType.FONT); } } diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 0566858f..03faf40a 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -14,8 +14,6 @@ // TODO: Rewrite VDropdown from scratch public class VDropdown extends VWidget implements VPaddingWidget { private final List items; - private VFont font; - private VFont hoverFont; private VColor itemHoverColor; private V4Int padding; @@ -27,8 +25,6 @@ public VDropdown(VeraApp app) { super(0, 0, 100, 16, app); items = new ArrayList<>(); - font = VFont.create(); - hoverFont = VFont.create(); itemHoverColor = VColor.white().sub(30); padding = new V4Int(5, 10); } @@ -36,7 +32,9 @@ public VDropdown(VeraApp app) { @Override public void render() { StyleState state = createStyleState(); + VColor backgroundColor = getStyle("background-color", state); + VFont font = getStyle("font", state); int x = getX(); int y = getY(); @@ -74,7 +72,7 @@ app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), ); } - Vera.renderer.drawText(app, textX, textY, 0, item.name, isHovered ? hoverFont : font); + Vera.renderer.drawText(app, textX, textY, 0, item.name, font); } } else { Vera.renderer.drawText(app, x, y, 0, getItems().get(selectedItem).name, font); @@ -97,22 +95,6 @@ public void setItemHoverColor(VColor itemHoveredColor) { this.itemHoverColor = itemHoveredColor; } - public VFont getHoverFont() { - return hoverFont; - } - - public void setHoverFont(VFont hoverFont) { - this.hoverFont = hoverFont; - } - - public VColor.ColorModifier modifyHoverFontColor() { - return new VColor.ColorModifier(hoverFont.getColor(), (color) -> setHoverFont(hoverFont.withColor(color))); - } - - public VFont.FontModifier modifyHoverFont() { - return new VFont.FontModifier(hoverFont, this::setHoverFont); - } - public VColor.ColorModifier modifyItemHoverColor() { return new VColor.ColorModifier(itemHoverColor, this::setItemHoverColor); } @@ -134,6 +116,8 @@ public int getHitboxWidth() { @Override public int getHitboxHeight() { + VFont font = getStyle("font", createStyleState()); + return !isFocused() ? font.getSize() / 2 + padding.get1() + padding.get2() : items.size() * (font.getSize() / 2 + itemSpacing) + padding.get1() + padding.get2(); @@ -217,6 +201,7 @@ public void handleBuiltinEvent(String event, Object... args) { private int getItemIndexAt(int mouseY) { if (mouseY < 0) return -1; + VFont font = getStyle("font", createStyleState()); int index = mouseY / (itemSpacing + font.getSize() / 2); return items.size() < index ? -1 : index; @@ -267,20 +252,12 @@ public void setSelectedItem(int selectedItem) { events.fire("vdropdown-item-switch", selectedItem); } - public VFont getFont() { - return font; - } - - public void setFont(VFont font) { - this.font = font; - } - public VFont.FontModifier modifyFont() { - return new VFont.FontModifier(font, this::setFont); + return app.styleSheet.modifyKeyAsFont(this, "font"); } public VColor.ColorModifier modifyFontColor() { - return new VColor.ColorModifier(font.getColor(), (color) -> setFont(font.withColor(color))); + return app.styleSheet.modifyKeyAsFontColor(this, "font"); } public void addItem(String name) { From 842a7bd8bee2b78da9a455160145bdfa296dfbf9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 22:50:04 +0200 Subject: [PATCH 263/661] VDropdown now correctly implements padding --- .../style/standard/DropdownStandardStyle.java | 3 +++ .../net/snackbag/vera/widget/VDropdown.java | 23 +++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java index ac6b9d26..5951d93e 100644 --- a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java @@ -1,5 +1,6 @@ package net.snackbag.vera.style.standard; +import net.snackbag.vera.core.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; @@ -14,11 +15,13 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VDropdown.class, "background-color", VColor.white()); sheet.setKey(VDropdown.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); sheet.setKey(VDropdown.class, "font", VFont.create()); + sheet.setKey(VDropdown.class, "padding", new V4Int(5, 10)); } @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("background-color", StyleValueType.COLOR); sheet.reserveType("font", StyleValueType.FONT); + sheet.reserveType("padding", StyleValueType.V4INT); } } diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 03faf40a..736eabab 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -4,7 +4,6 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.VItemSwitchEvent; -import net.snackbag.vera.modifier.VPaddingWidget; import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; @@ -12,10 +11,9 @@ import java.util.List; // TODO: Rewrite VDropdown from scratch -public class VDropdown extends VWidget implements VPaddingWidget { +public class VDropdown extends VWidget { private final List items; private VColor itemHoverColor; - private V4Int padding; private int selectedItem = 0; private int itemSpacing = 0; @@ -26,7 +24,6 @@ public VDropdown(VeraApp app) { items = new ArrayList<>(); itemHoverColor = VColor.white().sub(30); - padding = new V4Int(5, 10); } @Override @@ -101,38 +98,34 @@ public VColor.ColorModifier modifyItemHoverColor() { @Override public int getHitboxX() { + V4Int padding = getStyle("padding", createStyleState()); return getX() - padding.get3(); } @Override public int getHitboxY() { + V4Int padding = getStyle("padding", createStyleState()); return getY() - padding.get1(); } @Override public int getHitboxWidth() { + V4Int padding = getStyle("padding", createStyleState()); return width + padding.get3() + padding.get4(); } @Override public int getHitboxHeight() { - VFont font = getStyle("font", createStyleState()); + StyleState state = createStyleState(); + + VFont font = getStyle("font", state); + V4Int padding = getStyle("padding", state); return !isFocused() ? font.getSize() / 2 + padding.get1() + padding.get2() : items.size() * (font.getSize() / 2 + itemSpacing) + padding.get1() + padding.get2(); } - @Override - public V4Int getPadding() { - return padding; - } - - @Override - public void setPadding(V4Int padding) { - this.padding = padding; - } - @Override public void handleBuiltinEvent(String event, Object... args) { int x = getX(); From 5625c77b806df54e6ed1b1ed1774ef47f3ba7f17 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:02:10 +0200 Subject: [PATCH 264/661] allow quick modifications with state --- .../net/snackbag/vera/style/VStyleSheet.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 6606a996..23f60767 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -138,18 +138,39 @@ public void setKey(Class clazz, String key, Object value, @Nullable StyleStat } public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { - return new VColor.ColorModifier(getKey(widget, key), color -> setKey(widget, key, color)); + return modifyKeyAsColor(widget, key, StyleState.DEFAULT); + } + + public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + + @Nullable StyleState finalState = state; + return new VColor.ColorModifier(getKey(widget, key, state), color -> setKey(widget, key, color, finalState)); } public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key) { - return new VFont.FontModifier(getKey(widget, key), font -> setKey(widget, key, font)); + return modifyKeyAsFont(widget, key, StyleState.DEFAULT); + } + + public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + + @Nullable StyleState finalState = state; + return new VFont.FontModifier(getKey(widget, key, state), font -> setKey(widget, key, font, finalState)); } public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key) { + return modifyKeyAsColor(widget, key, StyleState.DEFAULT); + } + + public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + // this is cursed + @Nullable StyleState finalState = state; return new VColor.ColorModifier( - ((VFont) getKey(widget, key)).getColor(), - color -> modifyKeyAsFont(widget, key).color(color) + ((VFont) getKey(widget, key, state)).getColor(), + color -> modifyKeyAsFont(widget, key, finalState).color(color) ); } From c75db60865b2d258eafe76faa613f04def1078ef Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:18:34 +0200 Subject: [PATCH 265/661] fix mixup --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 23f60767..3303ff2e 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -160,7 +160,7 @@ public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key, @Nullab } public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key) { - return modifyKeyAsColor(widget, key, StyleState.DEFAULT); + return modifyKeyAsFontColor(widget, key, StyleState.DEFAULT); } public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key, @Nullable StyleState state) { From 70693aef6a0b0dffee384cfa9e358b6a8fb215d9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:18:42 +0200 Subject: [PATCH 266/661] basic modifiers! --- .../net/snackbag/vera/modifier/VHasFont.java | 25 +++++++++++++++++++ .../net/snackbag/vera/modifier/VModifier.java | 7 ++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/modifier/VHasFont.java create mode 100644 src/main/java/net/snackbag/vera/modifier/VModifier.java diff --git a/src/main/java/net/snackbag/vera/modifier/VHasFont.java b/src/main/java/net/snackbag/vera/modifier/VHasFont.java new file mode 100644 index 00000000..677c24eb --- /dev/null +++ b/src/main/java/net/snackbag/vera/modifier/VHasFont.java @@ -0,0 +1,25 @@ +package net.snackbag.vera.modifier; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFont; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.Nullable; + +public interface VHasFont extends VModifier { + default VFont.FontModifier modifyFont() { + return modifyFont(null); + } + + default VFont.FontModifier modifyFont(@Nullable StyleState state) { + return getApp().styleSheet.modifyKeyAsFont((VWidget) this, "font", state); + } + + default VColor.ColorModifier modifyFontColor() { + return modifyFontColor(null); + } + + default VColor.ColorModifier modifyFontColor(@Nullable StyleState state) { + return getApp().styleSheet.modifyKeyAsFontColor((VWidget) this, "font", state); + } +} diff --git a/src/main/java/net/snackbag/vera/modifier/VModifier.java b/src/main/java/net/snackbag/vera/modifier/VModifier.java new file mode 100644 index 00000000..4b7e714a --- /dev/null +++ b/src/main/java/net/snackbag/vera/modifier/VModifier.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.modifier; + +import net.snackbag.vera.core.VeraApp; + +public interface VModifier { + VeraApp getApp(); +} From 9dfcfacbd06a65fc8e4f87b8513689f0da5ca404 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:18:49 +0200 Subject: [PATCH 267/661] hover font colors --- .../java/net/snackbag/vera/widget/VDropdown.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 736eabab..bca00a5c 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.VItemSwitchEvent; +import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; @@ -11,7 +12,7 @@ import java.util.List; // TODO: Rewrite VDropdown from scratch -public class VDropdown extends VWidget { +public class VDropdown extends VWidget implements VHasFont { private final List items; private VColor itemHoverColor; @@ -245,14 +246,6 @@ public void setSelectedItem(int selectedItem) { events.fire("vdropdown-item-switch", selectedItem); } - public VFont.FontModifier modifyFont() { - return app.styleSheet.modifyKeyAsFont(this, "font"); - } - - public VColor.ColorModifier modifyFontColor() { - return app.styleSheet.modifyKeyAsFontColor(this, "font"); - } - public void addItem(String name) { items.add(new Item(name, null, null, null, null, null)); } @@ -281,6 +274,11 @@ public List getItems() { return items; } + @Override + public VeraApp getApp() { + return app; + } + public static class Item { private String name; private final @Nullable Runnable leftClick; From ca998a6c2461a19d87fa2254ad5a6ec530727790 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:18:54 +0200 Subject: [PATCH 268/661] correct padding --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index d5d7ce5e..78d38c7d 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -57,7 +57,7 @@ public void init() { label.onRightClickRelease(() -> setCursorShape(VCursorShape.DEFAULT)); label.onFilesDropped(System.out::println); - label.setPadding(5); + label.setStyle("padding", 5); label.move(10); label.setStyle("background-color", VColor.black()); label.modifyFont("font").color(VColor.white()); From 7ff12a1f42c31226c9cfa3d504d27c8423e4e54d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:19:31 +0200 Subject: [PATCH 269/661] only change font color when hovered --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 78d38c7d..c6ab383b 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -101,7 +101,7 @@ public void init() { dropdown.addItem("buger", () -> System.out.println("pressed")); dropdown.move(90); dropdown.setItemSpacing(16); - dropdown.modifyHoverFont().color(VColor.white()); + dropdown.modifyFont(StyleState.HOVERED).color(VColor.white()); dropdown.setItemHoverColor(VColor.black()); dropdown.onFocusStateChange(() -> System.out.println("focus state change: " + dropdown.isFocused())); From b35fb8b00a529075a6181022bfff320ee6bb2f8a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:33:43 +0200 Subject: [PATCH 270/661] add VHasPlaceholderFont --- .../vera/modifier/VHasPlaceholderFont.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java diff --git a/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java b/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java new file mode 100644 index 00000000..3067b287 --- /dev/null +++ b/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java @@ -0,0 +1,25 @@ +package net.snackbag.vera.modifier; + +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFont; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.Nullable; + +public interface VHasPlaceholderFont extends VModifier { + default VFont.FontModifier modifyPlaceholderFont() { + return modifyPlaceholderFont(null); + } + + default VFont.FontModifier modifyPlaceholderFont(@Nullable StyleState state) { + return getApp().styleSheet.modifyKeyAsFont((VWidget) this, "placeholder-font", state); + } + + default VColor.ColorModifier modifyPlaceholderFontColor() { + return modifyPlaceholderFontColor(null); + } + + default VColor.ColorModifier modifyPlaceholderFontColor(@Nullable StyleState state) { + return getApp().styleSheet.modifyKeyAsFontColor((VWidget) this, "placeholder-font", state); + } +} From 3784d12549a06e154e346632927c3e4497c5f4db Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:34:14 +0200 Subject: [PATCH 271/661] use VHasFont --- .../java/net/snackbag/vera/widget/VLabel.java | 16 +++++++--------- .../net/snackbag/vera/widget/VTabWidget.java | 16 +++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 28ae9986..962b7a3e 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -3,9 +3,10 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.flag.VHAlignmentFlag; +import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; -public class VLabel extends VWidget { +public class VLabel extends VWidget implements VHasFont { private String text; private VHAlignmentFlag alignment; @@ -70,16 +71,13 @@ public void adjustSize() { this.height = Vera.provider.getTextHeight(text, font); } - public VColor.ColorModifier modifyColor(String key) { - return app.styleSheet.modifyKeyAsColor(this, key); - } - - public VFont.FontModifier modifyFont(String key) { - return app.styleSheet.modifyKeyAsFont(this, key); + @Override + public VeraApp getApp() { + return app; } - public VColor.ColorModifier modifyFontColor(String key) { - return app.styleSheet.modifyKeyAsFontColor(this, key); + public VColor.ColorModifier modifyColor(String key) { + return app.styleSheet.modifyKeyAsColor(this, key); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index e8c4929e..59b36a4a 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -4,13 +4,14 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Consumer; -public class VTabWidget extends VWidget { +public class VTabWidget extends VWidget implements VHasFont { private final LinkedHashMap>> tabs = new LinkedHashMap<>(); private @Nullable Integer activeTab = null; private @Nullable Integer hoveredTab = null; @@ -232,14 +233,6 @@ public void setActiveTab(@Nullable Integer activeTab) { this.activeTab = activeTab; } - public VFont.FontModifier modifyFont() { - return app.styleSheet.modifyKeyAsFont(this, "font"); - } - - public VColor.ColorModifier modifyFontColor() { - return app.styleSheet.modifyKeyAsFontColor(this, "font"); - } - public VColor.ColorModifier modifyBackgroundColorSelected() { return app.styleSheet.modifyKeyAsColor(this, "background-color-selected"); } @@ -247,4 +240,9 @@ public VColor.ColorModifier modifyBackgroundColorSelected() { public VColor.ColorModifier modifyBackgroundColor() { return app.styleSheet.modifyKeyAsColor(this, "background-color"); } + + @Override + public VeraApp getApp() { + return app; + } } From 40252de22d09e713aa4bf1e53a21307098d42669 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:34:31 +0200 Subject: [PATCH 272/661] use VHasFont and VHasPlaceholderFont --- .../net/snackbag/vera/widget/VLineInput.java | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 766c37be..262986a8 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -5,12 +5,14 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.event.VCharLimitedEvent; +import net.snackbag.vera.modifier.VHasFont; +import net.snackbag.vera.modifier.VHasPlaceholderFont; import net.snackbag.vera.style.StyleState; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -public class VLineInput extends VWidget { +public class VLineInput extends VWidget implements VHasFont, VHasPlaceholderFont { private String text; private String placeholderText; @@ -102,20 +104,9 @@ public void handleBuiltinEvent(String event, Object... args) { } } - public VFont.FontModifier modifyFont() { - return app.styleSheet.modifyKeyAsFont(this, "font"); - } - - public VColor.ColorModifier modifyFontColor() { - return app.styleSheet.modifyKeyAsFontColor(this, "font"); - } - - public VFont.FontModifier modifyPlaceholderFont() { - return app.styleSheet.modifyKeyAsFont(this, "placeholder-font"); - } - - public VColor.ColorModifier modifyPlaceholderFontColor() { - return app.styleSheet.modifyKeyAsFontColor(this, "placeholder-font"); + @Override + public VeraApp getApp() { + return app; } public String getText() { From fbd116aae7e3c2a4ec786fb58989b43c9897a840 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:36:27 +0200 Subject: [PATCH 273/661] removal schedule notice for 1.12 --- src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java index 3a749622..432e7a59 100644 --- a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java +++ b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java @@ -1,7 +1,9 @@ package net.snackbag.vera.modifier; import net.snackbag.vera.core.V4Int; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.ScheduledForRemoval(inVersion = "1.11") @Deprecated(forRemoval = true, since = "1.10") public interface VPaddingWidget { V4Int getPadding(); From 7e0db689c1c68346be15164c64d6815fc4951feb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:44:47 +0200 Subject: [PATCH 274/661] undo removing itemHoverColor and add new notice --- src/main/java/net/snackbag/vera/widget/VDropdown.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index bca00a5c..a6bfd5c4 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -12,8 +12,11 @@ import java.util.List; // TODO: Rewrite VDropdown from scratch +// 16/7/2025 jesus christ what a shitty thing. dont even bother making this work nice. +// rewrite scheduled for once we have VCompound public class VDropdown extends VWidget implements VHasFont { private final List items; + public VFont itemHoverFont; private VColor itemHoverColor; private int selectedItem = 0; @@ -24,6 +27,7 @@ public VDropdown(VeraApp app) { super(0, 0, 100, 16, app); items = new ArrayList<>(); + itemHoverFont = VFont.create(); itemHoverColor = VColor.white().sub(30); } @@ -70,7 +74,7 @@ app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), ); } - Vera.renderer.drawText(app, textX, textY, 0, item.name, font); + Vera.renderer.drawText(app, textX, textY, 0, item.name, isHovered ? itemHoverFont : font); } } else { Vera.renderer.drawText(app, x, y, 0, getItems().get(selectedItem).name, font); From fc6c4ac90e19136f319dac6f04b5380210daf809 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:45:00 +0200 Subject: [PATCH 275/661] use correct new itemHoverFont --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index c6ab383b..8f2ff2b9 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -101,7 +101,7 @@ public void init() { dropdown.addItem("buger", () -> System.out.println("pressed")); dropdown.move(90); dropdown.setItemSpacing(16); - dropdown.modifyFont(StyleState.HOVERED).color(VColor.white()); + dropdown.itemHoverFont = VFont.create().withColor(VColor.white()); dropdown.setItemHoverColor(VColor.black()); dropdown.onFocusStateChange(() -> System.out.println("focus state change: " + dropdown.isFocused())); From 4c52050c512928a9790ba805c8a2f9a85fcc1fa6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 16 Jul 2025 23:45:14 +0200 Subject: [PATCH 276/661] modifyFontColor on label has been generalized and made more idiomatic --- .../java/net/snackbag/mcvera/test/TestApplication.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 8f2ff2b9..fcfb3d08 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -2,6 +2,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VFont; import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; @@ -60,7 +61,7 @@ public void init() { label.setStyle("padding", 5); label.move(10); label.setStyle("background-color", VColor.black()); - label.modifyFont("font").color(VColor.white()); + label.modifyFont().color(VColor.white()); label.adjustSize(); label.onHover(() -> { label.setText("Hovered"); @@ -73,7 +74,7 @@ public void init() { VLabel centerLabel = new VLabel("CENTER", 220, 10, 100, 16, this).alsoAdd(); centerLabel.setAlignment(VHAlignmentFlag.CENTER); centerLabel.setStyle("background-color", VColor.black()); - centerLabel.modifyFontColor("font").rgb(255, 255, 255); + centerLabel.modifyFontColor().rgb(255, 255, 255); centerLabel.setStyle("border-color", VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); centerLabel.setStyle("border-size", 5, 10, 8, 16); centerLabel.setStyle("cursor", VCursorShape.ALL_RESIZE); @@ -81,7 +82,7 @@ public void init() { VLabel rightLabel = new VLabel("RIGHT", 100, 10, 100, 16, this).alsoAdd(); rightLabel.setAlignment(VHAlignmentFlag.RIGHT); rightLabel.setStyle("background-color", VColor.black()); - rightLabel.modifyFontColor("font").rgb(255, 255, 255); + rightLabel.modifyFontColor().rgb(255, 255, 255); rightLabel.setStyle("border-color", VColor.white()); rightLabel.setStyle("border-size", 1); rightLabel.onRightClick(() -> System.out.println(Vera.openFileSelector("test", Path.of("/Volumes/Media"), null))); From 45f5159f935529d6ef4bc3011bb025f9f200aaff Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:45:56 +0200 Subject: [PATCH 277/661] add linear easing --- .../snackbag/vera/style/animation/easing/Easings.java | 5 +++++ .../snackbag/vera/style/animation/easing/VEasing.java | 5 +++++ .../vera/style/animation/easing/VLinearEasing.java | 10 ++++++++++ 3 files changed, 20 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/Easings.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java b/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java new file mode 100644 index 00000000..a61d46f8 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java @@ -0,0 +1,5 @@ +package net.snackbag.vera.style.animation.easing; + +public class Easings { + public static final VLinearEasing LINEAR = new VLinearEasing(); +} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java new file mode 100644 index 00000000..638cfe77 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java @@ -0,0 +1,5 @@ +package net.snackbag.vera.style.animation.easing; + +public abstract class VEasing { + public abstract float apply(float from, float to, float delta); +} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java new file mode 100644 index 00000000..bf43d734 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java @@ -0,0 +1,10 @@ +package net.snackbag.vera.style.animation.easing; + +public class VLinearEasing extends VEasing { + protected VLinearEasing() {} + + @Override + public float apply(float from, float to, float delta) { + return from + delta * (to - from); + } +} From 41f3b918b449e0d5b9f56bb99287d7bb2c5c65d7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:46:07 +0200 Subject: [PATCH 278/661] add base composite --- .../animation/pipeline/composite/Composite.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java diff --git a/src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java new file mode 100644 index 00000000..816dfe96 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java @@ -0,0 +1,16 @@ +package net.snackbag.vera.style.animation.pipeline.composite; + +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.pipeline.VeraPipeline; + +public abstract class Composite { + protected VeraPipeline pipeline; + + public void generateUniforms() {} + public abstract T apply(AnimationEngine engine, String style, StyleValueType type, T in); + + protected boolean isOf(StyleValueType type, String style, ) { + + } +} From 9613b85c942254c4c9ea68e8718afd2a37ee4dcc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:46:23 +0200 Subject: [PATCH 279/661] add basic pipeline --- .../animation/pipeline/VeraPipeline.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java diff --git a/src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java new file mode 100644 index 00000000..558df11a --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java @@ -0,0 +1,26 @@ +package net.snackbag.vera.style.animation.pipeline; + +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.pipeline.composite.Composite; + +import java.util.ArrayList; +import java.util.List; + +public class VeraPipeline { + public final VeraApp app; + private final List passes = new ArrayList<>(); + + public VeraPipeline(VeraApp app) { + this.app = app; + } + + public T applyCompositions(AnimationEngine engine, String style, StyleValueType type, T in) { + for (Composite pass : passes) { + in = pass.apply(engine, style, type, in); + } + + return in; + } +} From a3952eac5986f3b77dcd69dafa92aaf35999c2e2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:46:32 +0200 Subject: [PATCH 280/661] add basic animation engine --- .../vera/style/animation/AnimationEngine.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java new file mode 100644 index 00000000..4b97a659 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -0,0 +1,57 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.vera.Vera; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.LinkedHashSet; + +/** + * Per-widget handler for animations. This is after stylesheet.getKey, so there is no differentiation between + * widget-specific styles, class styles and standard styles. + */ +public class AnimationEngine { + public final VWidget widget; + private final LinkedHashSet activeAnimations = new LinkedHashSet<>(); + + private long cacheId = 0; + private final HashMap cache = new HashMap<>(); + + public AnimationEngine(VWidget widget) { + this.widget = widget; + } + + public boolean isActive(String name) { + return activeAnimations.stream().anyMatch(anim -> anim.name.equals(name)); + } + + public void activate(VAnimation animation) { + activeAnimations.add(animation); + } + + public @Nullable VAnimation getIfActive(String name) { + return activeAnimations.stream().filter(anim -> anim.name.equals(name)).findFirst().orElse(null); + } + + public VAnimation[] getAllActive() { + return activeAnimations.toArray(new VAnimation[0]); + } + + public void checkCache() { + if (cacheId != Vera.renderCacheId) { + cache.clear(); + cacheId = Vera.renderCacheId; + } + } + + public T animateStyle(String style, T value) { + checkCache(); + + if (cache.containsKey(style)) return (T) cache.get(style); + + StyleValueType type = StyleValueType.get(value, null); + return widget.app.pipeline.applyCompositions(this, style, type, value); + } +} From 3c0086c931026f1bb5f988b3d3ca043c8e88004a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:46:42 +0200 Subject: [PATCH 281/661] app now has pipeline --- src/main/java/net/snackbag/vera/core/VeraApp.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 73fa2a78..418d40c1 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -6,6 +6,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.style.animation.pipeline.VeraPipeline; import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -15,6 +16,7 @@ public abstract class VeraApp { public final VStyleSheet styleSheet = new VStyleSheet(); + public final VeraPipeline pipeline = new VeraPipeline(this); private final List> widgets; private final HashMap shortcuts; From f879100994d821835fab9ec601b8e843154e6adf Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:46:52 +0200 Subject: [PATCH 282/661] widget now has animation engine --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index a11864ae..a21422cc 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -6,6 +6,7 @@ import net.snackbag.vera.event.*; import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.animation.AnimationEngine; import net.snackbag.vera.util.DragHandler; import java.nio.file.Path; @@ -22,6 +23,7 @@ public abstract class VWidget> extends VElement { private boolean rightClickDown = false; private StyleState prevStyleState = StyleState.DEFAULT; + public final AnimationEngine animations = new AnimationEngine(this); public final LinkedHashSet classes = new LinkedHashSet<>(); public VWidget(int x, int y, int width, int height, VeraApp app) { From 9481b8b583faa635c0d62bd96837a4bfaf0ff0ff Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 17 Jul 2025 23:46:59 +0200 Subject: [PATCH 283/661] apply animations to getStyle --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index a21422cc..3d13ba43 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -61,7 +61,7 @@ public void setStyle(String key, StyleState state, V... value) { } public V getStyle(String key) { - return app.styleSheet.getKey(this, key); + return animations.animateStyle(key, app.styleSheet.getKey(this, key)); } public V getStyle(String key, StyleState state) { From b576c0ed8b3c5482ed9f02f22c0154f2bb4a1b49 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:38:32 +0200 Subject: [PATCH 284/661] add Once --- .../java/net/snackbag/vera/util/Once.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/util/Once.java diff --git a/src/main/java/net/snackbag/vera/util/Once.java b/src/main/java/net/snackbag/vera/util/Once.java new file mode 100644 index 00000000..d4600888 --- /dev/null +++ b/src/main/java/net/snackbag/vera/util/Once.java @@ -0,0 +1,36 @@ +package net.snackbag.vera.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class Once { + @Nullable + private T value; + + /** + * Sets the value if not already set + * + * @param value the value to set + * @throws UnsupportedOperationException if value is already set + * @throws NullPointerException if given value is null + */ + public void set(@NotNull T value) { + Objects.requireNonNull(value, "value must not be null"); + + if (this.value != null) throw new UnsupportedOperationException("Cannot set value that is already set"); + this.value = value; + } + + /** + * Gets the set value if not null + * + * @return the value + * @throws NullPointerException if value is null + */ + public @NotNull T get() { + if (value == null) throw new NullPointerException("Cannot get value that isn't set"); + return value; + } +} From 443a25d432ad1b7db897db3bb72c2ff157bf47a1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:43:50 +0200 Subject: [PATCH 285/661] add setSafe method --- src/main/java/net/snackbag/vera/util/Once.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/net/snackbag/vera/util/Once.java b/src/main/java/net/snackbag/vera/util/Once.java index d4600888..d91c3b87 100644 --- a/src/main/java/net/snackbag/vera/util/Once.java +++ b/src/main/java/net/snackbag/vera/util/Once.java @@ -23,6 +23,20 @@ public void set(@NotNull T value) { this.value = value; } + /** + * Sets the value if not already set without throwing exceptions + * + * @param value the value to set + * @return true if value could be set, false if unable to set + */ + public boolean setSafe(T value) { + if (value == null) return false; + if (this.value != null) return false; + + this.value = value; + return true; + } + /** * Gets the set value if not null * From 514e531ebaf09d06740590071b0cac6b80190e89 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:45:37 +0200 Subject: [PATCH 286/661] add equals and hashCode --- src/main/java/net/snackbag/vera/util/Once.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/net/snackbag/vera/util/Once.java b/src/main/java/net/snackbag/vera/util/Once.java index d91c3b87..03ce3587 100644 --- a/src/main/java/net/snackbag/vera/util/Once.java +++ b/src/main/java/net/snackbag/vera/util/Once.java @@ -47,4 +47,15 @@ public boolean setSafe(T value) { if (value == null) throw new NullPointerException("Cannot get value that isn't set"); return value; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Once once)) return false; + return Objects.equals(value, once.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } } From 675a95a76ddbb41e37d7cc1f5ba1c85c078455c9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:45:42 +0200 Subject: [PATCH 287/661] value default null --- src/main/java/net/snackbag/vera/util/Once.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/util/Once.java b/src/main/java/net/snackbag/vera/util/Once.java index 03ce3587..5d6f318c 100644 --- a/src/main/java/net/snackbag/vera/util/Once.java +++ b/src/main/java/net/snackbag/vera/util/Once.java @@ -7,7 +7,7 @@ public class Once { @Nullable - private T value; + private T value = null; /** * Sets the value if not already set From 791be1b54bdbca0c12bc799661538e7d564052c1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:45:46 +0200 Subject: [PATCH 288/661] isSet method --- src/main/java/net/snackbag/vera/util/Once.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/util/Once.java b/src/main/java/net/snackbag/vera/util/Once.java index 5d6f318c..4a6ac64c 100644 --- a/src/main/java/net/snackbag/vera/util/Once.java +++ b/src/main/java/net/snackbag/vera/util/Once.java @@ -37,6 +37,15 @@ public boolean setSafe(T value) { return true; } + /** + * Checks whether the value is set or not + * + * @return if the value is set + */ + public boolean isSet() { + return value != null; + } + /** * Gets the set value if not null * From bb2444b9f813048a07bd32b018cdb6bac468ae9f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:47:11 +0200 Subject: [PATCH 289/661] make pipeline be once --- .../animation/{pipeline => }/composite/Composite.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename src/main/java/net/snackbag/vera/style/animation/{pipeline => }/composite/Composite.java (65%) diff --git a/src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java similarity index 65% rename from src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java rename to src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 816dfe96..27579e64 100644 --- a/src/main/java/net/snackbag/vera/style/animation/pipeline/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -1,11 +1,12 @@ -package net.snackbag.vera.style.animation.pipeline.composite; +package net.snackbag.vera.style.animation.composite; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.animation.AnimationEngine; -import net.snackbag.vera.style.animation.pipeline.VeraPipeline; +import net.snackbag.vera.style.animation.VeraPipeline; +import net.snackbag.vera.util.Once; public abstract class Composite { - protected VeraPipeline pipeline; + public Once pipeline; public void generateUniforms() {} public abstract T apply(AnimationEngine engine, String style, StyleValueType type, T in); From 602b97bd0ee71eb5f1f325f39096b592d88fa4cc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:47:17 +0200 Subject: [PATCH 290/661] remove isOf method --- .../snackbag/vera/style/animation/composite/Composite.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 27579e64..aa6cdee9 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -10,8 +10,4 @@ public abstract class Composite { public void generateUniforms() {} public abstract T apply(AnimationEngine engine, String style, StyleValueType type, T in); - - protected boolean isOf(StyleValueType type, String style, ) { - - } } From cf649e974ddede8596a6f444c70fa7bfc7974b24 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:49:43 +0200 Subject: [PATCH 291/661] empty composites --- .../style/animation/composite/AnimationComposite.java | 11 +++++++++++ .../style/animation/composite/DiscardComposite.java | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java new file mode 100644 index 00000000..1a4570e6 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -0,0 +1,11 @@ +package net.snackbag.vera.style.animation.composite; + +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.AnimationEngine; + +public class AnimationComposite extends Composite { + @Override + public T apply(AnimationEngine engine, String style, StyleValueType type, T in) { + return in; + } +} diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java new file mode 100644 index 00000000..b24e7ef7 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java @@ -0,0 +1,11 @@ +package net.snackbag.vera.style.animation.composite; + +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.AnimationEngine; + +public class DiscardComposite extends Composite { + @Override + public T apply(AnimationEngine engine, String style, StyleValueType type, T in) { + return in; + } +} From e9e479465b1a827335888ce04ce921874c324672 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:52:44 +0200 Subject: [PATCH 292/661] applyCompositions -> applyComposites --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 4b97a659..de3333c8 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -52,6 +52,6 @@ public T animateStyle(String style, T value) { if (cache.containsKey(style)) return (T) cache.get(style); StyleValueType type = StyleValueType.get(value, null); - return widget.app.pipeline.applyCompositions(this, style, type, value); + return widget.app.pipeline.applyComposites(this, style, type, value); } } From 45ed7d94a2218f17c56fa352b992585ea6c6e15f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:52:51 +0200 Subject: [PATCH 293/661] add frameTime to composite --- .../net/snackbag/vera/style/animation/composite/Composite.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index aa6cdee9..4bd0fb71 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -7,6 +7,7 @@ public abstract class Composite { public Once pipeline; + public long frameTime = 0; public void generateUniforms() {} public abstract T apply(AnimationEngine engine, String style, StyleValueType type, T in); From c33bc196ef234b6b936484396079b026ded8da8a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:52:56 +0200 Subject: [PATCH 294/661] basic VAnimation --- .../vera/style/animation/VAnimation.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/VAnimation.java diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java new file mode 100644 index 00000000..359845c6 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -0,0 +1,20 @@ +package net.snackbag.vera.style.animation; + +import java.util.ArrayList; +import java.util.List; + +public class VAnimation { + public final String name; + public final int discardTime; + + private final List keyframes = new ArrayList<>(); + + public VAnimation(String name, int discardTime) { + this.name = name; + this.discardTime = discardTime; + } + + public void addKeyframe(VKeyframe keyframe) { + this.keyframes.add(keyframe); + } +} From 09709fd8dc339932fc5b5a10e2e3c6ed4c19bc0a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:53:22 +0200 Subject: [PATCH 295/661] implement composite uniform generator --- .../vera/style/animation/VeraPipeline.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java new file mode 100644 index 00000000..007f2b14 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java @@ -0,0 +1,46 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.composite.Composite; + +import java.util.ArrayList; +import java.util.List; + +public class VeraPipeline { + public final VeraApp app; + private final List passes = new ArrayList<>(); + + public VeraPipeline(VeraApp app) { + this.app = app; + } + + public void addPass(Composite pass) { + setupPass(pass); + this.passes.add(pass); + } + + public void addPass(int index, Composite pass) { + setupPass(pass); + this.passes.add(index, pass); + } + + private void setupPass(Composite pass) { + boolean unique = pass.pipeline.setSafe(this); + if (!unique) throw new UnsupportedOperationException("Cannot setup composite pass if composite is already bound to pipeline"); + } + + public T applyComposites(AnimationEngine engine, String style, StyleValueType type, T in) { + for (Composite pass : passes) { + if (pass.frameTime != Vera.renderCacheId) { + pass.frameTime = Vera.renderCacheId; + pass.generateUniforms(); + } + + in = pass.apply(engine, style, type, in); + } + + return in; + } +} From 139fae9073683582956029dba72588ad4202667c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 13:53:26 +0200 Subject: [PATCH 296/661] move VeraPipeline.java --- .../java/net/snackbag/vera/core/VeraApp.java | 2 +- .../animation/pipeline/VeraPipeline.java | 26 ------------------- 2 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 418d40c1..9a31918a 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -6,7 +6,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; -import net.snackbag.vera.style.animation.pipeline.VeraPipeline; +import net.snackbag.vera.style.animation.VeraPipeline; import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java deleted file mode 100644 index 558df11a..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/pipeline/VeraPipeline.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.snackbag.vera.style.animation.pipeline; - -import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.AnimationEngine; -import net.snackbag.vera.style.animation.pipeline.composite.Composite; - -import java.util.ArrayList; -import java.util.List; - -public class VeraPipeline { - public final VeraApp app; - private final List passes = new ArrayList<>(); - - public VeraPipeline(VeraApp app) { - this.app = app; - } - - public T applyCompositions(AnimationEngine engine, String style, StyleValueType type, T in) { - for (Composite pass : passes) { - in = pass.apply(engine, style, type, in); - } - - return in; - } -} From 64ff49b50ab86ec07eb01e4111ea20905148a2cb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 19:43:35 +0200 Subject: [PATCH 297/661] docstring for uniforms --- .../net/snackbag/vera/style/animation/composite/Composite.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 4bd0fb71..499ba6b0 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -9,6 +9,9 @@ public abstract class Composite { public Once pipeline; public long frameTime = 0; + /** + * This method is called per-frame and is entirely independent of the given style. Therefore, the name uniform. + */ public void generateUniforms() {} public abstract T apply(AnimationEngine engine, String style, StyleValueType type, T in); } From b0a6246a23819c2dacb46eaed075eededd94826f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 19:43:39 +0200 Subject: [PATCH 298/661] ctx --- .../snackbag/vera/style/animation/composite/Composite.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 499ba6b0..a373c9a6 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -13,5 +13,8 @@ public abstract class Composite { * This method is called per-frame and is entirely independent of the given style. Therefore, the name uniform. */ public void generateUniforms() {} - public abstract T apply(AnimationEngine engine, String style, StyleValueType type, T in); + public abstract T apply(Context ctx); + + public record Context(AnimationEngine engine, String style, StyleValueType type, T in) { + } } From f43bd7b815d588d1f0c177449f9f1e06030c5c00 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 19:43:49 +0200 Subject: [PATCH 299/661] update composites to fit new design --- .../vera/style/animation/composite/AnimationComposite.java | 7 ++----- .../vera/style/animation/composite/DiscardComposite.java | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 1a4570e6..a14441ea 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -1,11 +1,8 @@ package net.snackbag.vera.style.animation.composite; -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.AnimationEngine; - public class AnimationComposite extends Composite { @Override - public T apply(AnimationEngine engine, String style, StyleValueType type, T in) { - return in; + public T apply(Context ctx) { + return ctx.in(); } } diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java index b24e7ef7..ee5bb042 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java @@ -1,11 +1,8 @@ package net.snackbag.vera.style.animation.composite; -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.AnimationEngine; - public class DiscardComposite extends Composite { @Override - public T apply(AnimationEngine engine, String style, StyleValueType type, T in) { - return in; + public T apply(Context ctx) { + return ctx.in(); } } From 32c8d0061459f3cbcb4c5f43c4b23b3df09a9c6d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 19:43:59 +0200 Subject: [PATCH 300/661] give context to pass --- .../java/net/snackbag/vera/style/animation/VeraPipeline.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java index 007f2b14..784ceef6 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java +++ b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java @@ -32,13 +32,15 @@ private void setupPass(Composite pass) { } public T applyComposites(AnimationEngine engine, String style, StyleValueType type, T in) { + Composite.Context ctx = new Composite.Context(engine,style, type, in); + for (Composite pass : passes) { if (pass.frameTime != Vera.renderCacheId) { pass.frameTime = Vera.renderCacheId; pass.generateUniforms(); } - in = pass.apply(engine, style, type, in); + in = pass.apply(ctx); } return in; From 794b3e39d39950388420e47cf50683f5dcef0ae3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:35:09 +0200 Subject: [PATCH 301/661] make require app --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 359845c6..365a720c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -5,7 +5,10 @@ public class VAnimation { public final String name; - public final int discardTime; + public final VeraApp app; + + public final int unwindTime; + public final LoopMode loopMode; private final List keyframes = new ArrayList<>(); From c59c2bd64202b62470d17bcb11a13ebc2061d693 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:35:15 +0200 Subject: [PATCH 302/661] add LoopMode --- .../java/net/snackbag/vera/style/animation/LoopMode.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/LoopMode.java diff --git a/src/main/java/net/snackbag/vera/style/animation/LoopMode.java b/src/main/java/net/snackbag/vera/style/animation/LoopMode.java new file mode 100644 index 00000000..4beded31 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/LoopMode.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.style.animation; + +public enum LoopMode { + NONE, + REPEAT, + REPEAT_REVERSE +} From 66a445f8214769a207dc6b470373f452104efb60 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:35:29 +0200 Subject: [PATCH 303/661] discardTime -> unwindTime --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 365a720c..a2e73246 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -12,9 +12,12 @@ public class VAnimation { private final List keyframes = new ArrayList<>(); - public VAnimation(String name, int discardTime) { + public VAnimation(String name, int unwindTime, LoopMode loopMode, VeraApp app) { this.name = name; - this.discardTime = discardTime; + this.unwindTime = unwindTime; + this.loopMode = loopMode; + + this.app = app; } public void addKeyframe(VKeyframe keyframe) { From fc32273c6eefb3f0e0e893d27bdfcaa76a36a910 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:35:36 +0200 Subject: [PATCH 304/661] simple builder --- .../vera/style/animation/VAnimation.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index a2e73246..2332c052 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -1,7 +1,10 @@ package net.snackbag.vera.style.animation; +import net.snackbag.vera.core.VeraApp; + import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; public class VAnimation { public final String name; @@ -23,4 +26,47 @@ public VAnimation(String name, int unwindTime, LoopMode loopMode, VeraApp app) { public void addKeyframe(VKeyframe keyframe) { this.keyframes.add(keyframe); } + + public static class Builder { + private final String name; + private final VeraApp app; + + private LoopMode loopMode = LoopMode.NONE; + private int unwindTime = 0; + + private final List keyframes = new ArrayList<>(); + + public Builder(VeraApp app, String name) { + this.name = name; + this.app = app; + } + + public Builder loop(LoopMode mode) { + this.loopMode = mode; + return this; + } + + public Builder unwindTime(int ms) { + this.unwindTime = ms; + return this; + } + + public Builder keyframe(int transitionMs, Consumer frame, int stayMs) { + VKeyframe target = new VKeyframe(transitionMs, stayMs); + frame.accept(target); + + keyframes.add(target); + return this; + } + + public VAnimation build() { + VAnimation animation = new VAnimation(name, unwindTime, loopMode, app); + + for (VKeyframe frame : keyframes) { + animation.addKeyframe(frame); + } + + return new VAnimation(name, unwindTime, loopMode, app); + } + } } From 4cc62d3b5c5f0c732b8fa43695ba31c3d4ecd604 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:36:28 +0200 Subject: [PATCH 305/661] basic keyframe --- .../vera/style/animation/VKeyframe.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/VKeyframe.java diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java new file mode 100644 index 00000000..cb60621a --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -0,0 +1,27 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.util.Once; +import oshi.util.tuples.Pair; + +import java.util.HashMap; + +public class VKeyframe { + protected final Once animation = new Once<>(); + protected final int transitionTime; + protected final int stayTime; + protected final HashMap> styles = new HashMap<>(); + + public VKeyframe(int transitionTime, int stayTime) { + this.transitionTime = transitionTime; + this.stayTime = stayTime; + } + + public void style(String key, Object value) { + StyleValueType reservation = animation.get().app.styleSheet.getReservation(key); + if (reservation == null) throw new UnsupportedOperationException("Cannot set keyframe style to unreserved style key"); + + Object converted = StyleValueType.convert(value, reservation); + styles.put(key, new Pair<>(reservation, converted)); + } +} From e479fc6117682dcf47a945a1d425fbfce00d360b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:37:46 +0200 Subject: [PATCH 306/661] add easeIn easing --- src/main/java/net/snackbag/vera/style/animation/VKeyframe.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index cb60621a..d09a6ab1 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -1,6 +1,7 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.util.Once; import oshi.util.tuples.Pair; @@ -12,6 +13,8 @@ public class VKeyframe { protected final int stayTime; protected final HashMap> styles = new HashMap<>(); + public VEasing easeIn; + public VKeyframe(int transitionTime, int stayTime) { this.transitionTime = transitionTime; this.stayTime = stayTime; From e5224a9a5fc27adb71aff544fee8e32c72ab0491 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:40:49 +0200 Subject: [PATCH 307/661] add unwindEasing --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 2332c052..8902fc0d 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -1,6 +1,8 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.animation.easing.Easings; +import net.snackbag.vera.style.animation.easing.VEasing; import java.util.ArrayList; import java.util.List; @@ -11,13 +13,15 @@ public class VAnimation { public final VeraApp app; public final int unwindTime; + public final VEasing unwindEasing; public final LoopMode loopMode; private final List keyframes = new ArrayList<>(); - public VAnimation(String name, int unwindTime, LoopMode loopMode, VeraApp app) { + public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode loopMode, VeraApp app) { this.name = name; this.unwindTime = unwindTime; + this.unwindEasing = unwindEasing; this.loopMode = loopMode; this.app = app; From 3b4e0a06f930ebc22ed24b59a8a6424f3de358c5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:40:59 +0200 Subject: [PATCH 308/661] unwindEasing in builder --- .../net/snackbag/vera/style/animation/VAnimation.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 8902fc0d..f0110408 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -37,6 +37,7 @@ public static class Builder { private LoopMode loopMode = LoopMode.NONE; private int unwindTime = 0; + private VEasing unwindEasing = Easings.LINEAR; private final List keyframes = new ArrayList<>(); @@ -55,6 +56,11 @@ public Builder unwindTime(int ms) { return this; } + public Builder unwindEasing(VEasing easing) { + this.unwindEasing = easing; + return this; + } + public Builder keyframe(int transitionMs, Consumer frame, int stayMs) { VKeyframe target = new VKeyframe(transitionMs, stayMs); frame.accept(target); @@ -64,7 +70,7 @@ public Builder keyframe(int transitionMs, Consumer frame, int stayMs) } public VAnimation build() { - VAnimation animation = new VAnimation(name, unwindTime, loopMode, app); + VAnimation animation = new VAnimation(name, unwindTime, unwindEasing, loopMode, app); for (VKeyframe frame : keyframes) { animation.addKeyframe(frame); From 5aef0816297c458ecbab5e271ce92e7f93c4c63f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:41:05 +0200 Subject: [PATCH 309/661] actually give correct animation --- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index f0110408..b9cffa56 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -76,7 +76,7 @@ public VAnimation build() { animation.addKeyframe(frame); } - return new VAnimation(name, unwindTime, loopMode, app); + return animation; } } } From 1cdbc5da382101a6dafc4f6d25910aa51dd6c99e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 18 Jul 2025 20:41:37 +0200 Subject: [PATCH 310/661] make easeIn @NotNull and default to Easings.LINEAR --- .../java/net/snackbag/vera/style/animation/VKeyframe.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index d09a6ab1..c84b4600 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -1,8 +1,10 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.util.Once; +import org.jetbrains.annotations.NotNull; import oshi.util.tuples.Pair; import java.util.HashMap; @@ -13,7 +15,7 @@ public class VKeyframe { protected final int stayTime; protected final HashMap> styles = new HashMap<>(); - public VEasing easeIn; + public @NotNull VEasing easeIn = Easings.LINEAR; public VKeyframe(int transitionTime, int stayTime) { this.transitionTime = transitionTime; From 0a6d3eb0c4bd1d4138884b38bf7d966346a1e9de Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 18:09:41 +0200 Subject: [PATCH 311/661] add getCombinedTime --- .../java/net/snackbag/vera/style/animation/VKeyframe.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index c84b4600..8cb771e9 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -29,4 +29,8 @@ public void style(String key, Object value) { Object converted = StyleValueType.convert(value, reservation); styles.put(key, new Pair<>(reservation, converted)); } + + public int getCombinedTime() { + return transitionTime + stayTime; + } } From f206798933155942155cfb647648b0d3da668547 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 18:22:18 +0200 Subject: [PATCH 312/661] me sound smart --- .../net/snackbag/vera/style/animation/composite/Composite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index a373c9a6..dbf2a41c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -10,7 +10,7 @@ public abstract class Composite { public long frameTime = 0; /** - * This method is called per-frame and is entirely independent of the given style. Therefore, the name uniform. + * This method is called per-frame and is entirely independent of the given style. Hence, the name uniform. */ public void generateUniforms() {} public abstract T apply(Context ctx); From e7964288055e090cf4e0ced3828c7ed9cd6ddbb8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 18:24:11 +0200 Subject: [PATCH 313/661] add isNewFrame argument --- .../java/net/snackbag/vera/style/animation/VeraPipeline.java | 4 +++- .../vera/style/animation/composite/AnimationComposite.java | 2 +- .../snackbag/vera/style/animation/composite/Composite.java | 2 +- .../vera/style/animation/composite/DiscardComposite.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java index 784ceef6..d66213ac 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java +++ b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java @@ -33,14 +33,16 @@ private void setupPass(Composite pass) { public T applyComposites(AnimationEngine engine, String style, StyleValueType type, T in) { Composite.Context ctx = new Composite.Context(engine,style, type, in); + boolean isNewFrame = false; for (Composite pass : passes) { if (pass.frameTime != Vera.renderCacheId) { pass.frameTime = Vera.renderCacheId; pass.generateUniforms(); + isNewFrame = true; } - in = pass.apply(ctx); + in = pass.apply(ctx, isNewFrame); } return in; diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index a14441ea..f02ac1eb 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -2,7 +2,7 @@ public class AnimationComposite extends Composite { @Override - public T apply(Context ctx) { + public T apply(Context ctx, boolean isNewFrame) { return ctx.in(); } } diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index dbf2a41c..f8dccb2f 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -13,7 +13,7 @@ public abstract class Composite { * This method is called per-frame and is entirely independent of the given style. Hence, the name uniform. */ public void generateUniforms() {} - public abstract T apply(Context ctx); + public abstract T apply(Context ctx, boolean isNewFrame); public record Context(AnimationEngine engine, String style, StyleValueType type, T in) { } diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java index ee5bb042..09a76a70 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java @@ -2,7 +2,7 @@ public class DiscardComposite extends Composite { @Override - public T apply(Context ctx) { + public T apply(Context ctx, boolean isNewFrame) { return ctx.in(); } } From 401e59d21b48b8d398f9e007a5fad1d554013ed4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 18:29:01 +0200 Subject: [PATCH 314/661] actually load composites --- src/main/java/net/snackbag/vera/core/VeraApp.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 9a31918a..ff5bf5df 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -7,6 +7,8 @@ import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.style.animation.VeraPipeline; +import net.snackbag.vera.style.animation.composite.AnimationComposite; +import net.snackbag.vera.style.animation.composite.DiscardComposite; import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -56,6 +58,13 @@ public VeraApp(boolean mouseRequired) { this.visible = false; setPositioning(VWindowPositioningFlag.SCREEN); + + loadComposites(); + } + + public void loadComposites() { + pipeline.addPass(new AnimationComposite()); + pipeline.addPass(new DiscardComposite()); } public void setCursorVisible(boolean cursorVisible) { From 0135ddc1332af654fe476a5556e6fc3ce09f63a7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 18:29:31 +0200 Subject: [PATCH 315/661] add affects method --- .../java/net/snackbag/vera/style/animation/VKeyframe.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index 8cb771e9..db13f9a8 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -33,4 +33,8 @@ public void style(String key, Object value) { public int getCombinedTime() { return transitionTime + stayTime; } + + public boolean affects(String style) { + return styles.containsKey(style); + } } From b8f70e2da850ad066e24233417e8e4520165db99 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 20:51:14 +0200 Subject: [PATCH 316/661] no more getCombinedTime, the class is final anyways --- .../java/net/snackbag/vera/style/animation/VKeyframe.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index db13f9a8..e456eb24 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -13,6 +13,7 @@ public class VKeyframe { protected final Once animation = new Once<>(); protected final int transitionTime; protected final int stayTime; + protected final int cumulatedTime; protected final HashMap> styles = new HashMap<>(); public @NotNull VEasing easeIn = Easings.LINEAR; @@ -20,6 +21,7 @@ public class VKeyframe { public VKeyframe(int transitionTime, int stayTime) { this.transitionTime = transitionTime; this.stayTime = stayTime; + this.cumulatedTime = this.transitionTime + this.stayTime; } public void style(String key, Object value) { @@ -30,10 +32,6 @@ public void style(String key, Object value) { styles.put(key, new Pair<>(reservation, converted)); } - public int getCombinedTime() { - return transitionTime + stayTime; - } - public boolean affects(String style) { return styles.containsKey(style); } From 0624e4796dd0d05a9db3bc47a754b283851c4171 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:34:40 +0200 Subject: [PATCH 317/661] LinkedHashSet -> HashMap --- .../snackbag/vera/style/animation/AnimationEngine.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index de3333c8..3f75fd0b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -14,7 +14,7 @@ */ public class AnimationEngine { public final VWidget widget; - private final LinkedHashSet activeAnimations = new LinkedHashSet<>(); + private final HashMap activeAnimations = new HashMap<>(); private long cacheId = 0; private final HashMap cache = new HashMap<>(); @@ -24,7 +24,7 @@ public AnimationEngine(VWidget widget) { } public boolean isActive(String name) { - return activeAnimations.stream().anyMatch(anim -> anim.name.equals(name)); + return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } public void activate(VAnimation animation) { @@ -32,11 +32,11 @@ public void activate(VAnimation animation) { } public @Nullable VAnimation getIfActive(String name) { - return activeAnimations.stream().filter(anim -> anim.name.equals(name)).findFirst().orElse(null); + return activeAnimations.keySet().stream().filter(anim -> anim.name.equals(name)).findFirst().orElse(null); } public VAnimation[] getAllActive() { - return activeAnimations.toArray(new VAnimation[0]); + return activeAnimations.keySet().toArray(new VAnimation[0]); } public void checkCache() { From 9f58774d0b5618761903fdcdf777845af1cb49a4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:34:48 +0200 Subject: [PATCH 318/661] activate now doesnt override by default --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 3f75fd0b..dd90a8e6 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -28,7 +28,12 @@ public boolean isActive(String name) { } public void activate(VAnimation animation) { - activeAnimations.add(animation); + activate(animation, false); + } + + public void activate(VAnimation animation, boolean override) { + if (!override && activeAnimations.containsKey(animation)) return; + activeAnimations.put(animation, System.currentTimeMillis()); } public @Nullable VAnimation getIfActive(String name) { From 64d4acbef1e0a9e6b6f938009bfaa1a1e1e60e63 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:34:57 +0200 Subject: [PATCH 319/661] add getTimeSinceActive method --- .../snackbag/vera/style/animation/AnimationEngine.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index dd90a8e6..57bd0320 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -59,4 +59,14 @@ public T animateStyle(String style, T value) { StyleValueType type = StyleValueType.get(value, null); return widget.app.pipeline.applyComposites(this, style, type, value); } + + /** + * Gets the time an animation has been active since, if it isn't active at all it will return -1 + * + * @param animation the animation to check + * @return when the animation was started + */ + public long getTimeSinceActive(VAnimation animation) { + return activeAnimations.getOrDefault(animation, -1L); + } } From 25bb522dcb850f72db7a211e538f66b765a66aed Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:35:18 +0200 Subject: [PATCH 320/661] add docstring --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index b9cffa56..619c91aa 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -8,6 +8,13 @@ import java.util.List; import java.util.function.Consumer; +/** + * Represents an animation template scoped to a specific {@link VeraApp} instance. + *

+ * Animations are app-specific and should not be shared across multiple apps. + * If you need to reuse an animation in different apps, define a method that + * generates a new instance of the animation for each target app. + */ public class VAnimation { public final String name; public final VeraApp app; From a66f7a93e038d4981fba78a66c94f518e8f6d2a0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:35:33 +0200 Subject: [PATCH 321/661] count the amount of times a style is modified --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 619c91aa..6ea7b70f 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -5,6 +5,7 @@ import net.snackbag.vera.style.animation.easing.VEasing; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.function.Consumer; @@ -24,6 +25,7 @@ public class VAnimation { public final LoopMode loopMode; private final List keyframes = new ArrayList<>(); + private final HashMap styleAffections = new HashMap<>(); public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode loopMode, VeraApp app) { this.name = name; @@ -36,6 +38,10 @@ public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode lo public void addKeyframe(VKeyframe keyframe) { this.keyframes.add(keyframe); + + for (String key : keyframe.styles.keySet()) { + styleAffections.merge(key, 1, Integer::sum); + } } public static class Builder { From 0b323e325cd1c98480e672ed2b987dab894f46e7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:36:51 +0200 Subject: [PATCH 322/661] add affects method --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 6ea7b70f..16049794 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -44,6 +44,10 @@ public void addKeyframe(VKeyframe keyframe) { } } + public boolean affects(String style) { + return styleAffections.containsKey(style); + } + public static class Builder { private final String name; private final VeraApp app; From f155bf8e5102f3b191b0b07055738a5c92c85f73 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 21:39:33 +0200 Subject: [PATCH 323/661] basic animation composite --- .../composite/AnimationComposite.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index f02ac1eb..f88fe1b2 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -1,8 +1,46 @@ package net.snackbag.vera.style.animation.composite; +import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.VAnimation; + +import java.util.HashMap; + public class AnimationComposite extends Composite { + private final HashMap animations = new HashMap<>(); + private final HashMap> precalculatedStyles = new HashMap<>(); + @Override public T apply(Context ctx, boolean isNewFrame) { - return ctx.in(); + AnimationEngine engine = ctx.engine(); + + if (isNewFrame) { + animations.put(engine, engine.getAllActive()); + precalculatedStyles.clear(); + } + + T out = ctx.in(); + VAnimation[] localAnimations = animations.get(engine); + + // If no animations active, return the input + if (localAnimations == null) return out; + + String style = ctx.style(); + + for (VAnimation animation : localAnimations) { + if (!animation.affects(style)) continue; + if (isNewFrame) { + precalculatedStyles.put( + animation, + animation.calculateStyle( + style, + System.currentTimeMillis() - engine.getTimeSinceActive(animation) + ) + ); + } + + out = (T) precalculatedStyles.get(animation).get(style); + } + + return out; } } From 812dd5bb40b2af4a2616074f4cba0d1f64fb4441 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 22:04:45 +0200 Subject: [PATCH 324/661] better animation ahdnling --- .../animation/composite/AnimationComposite.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index f88fe1b2..a8181347 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -28,9 +28,15 @@ public T apply(Context ctx, boolean isNewFrame) { for (VAnimation animation : localAnimations) { if (!animation.affects(style)) continue; - if (isNewFrame) { - precalculatedStyles.put( - animation, + + boolean animationCached = precalculatedStyles.containsKey(animation); + + // If animation isn't cached or the key isn't cached + if (!animationCached || !precalculatedStyles.get(animation).containsKey(style)) { + if (!animationCached) precalculatedStyles.put(animation, new HashMap<>()); + + precalculatedStyles.get(animation).put( + style, animation.calculateStyle( style, System.currentTimeMillis() - engine.getTimeSinceActive(animation) From b668e077ee804bb1ed3f8b105b80a7da38f4b1c4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 22:04:57 +0200 Subject: [PATCH 325/661] add getTotalTime --- .../net/snackbag/vera/style/animation/VAnimation.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 16049794..1d370a5f 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -27,6 +27,8 @@ public class VAnimation { private final List keyframes = new ArrayList<>(); private final HashMap styleAffections = new HashMap<>(); + private int totalTime = 0; + public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode loopMode, VeraApp app) { this.name = name; this.unwindTime = unwindTime; @@ -42,12 +44,18 @@ public void addKeyframe(VKeyframe keyframe) { for (String key : keyframe.styles.keySet()) { styleAffections.merge(key, 1, Integer::sum); } + + totalTime += keyframe.cumulatedTime; } public boolean affects(String style) { return styleAffections.containsKey(style); } + public int getTotalTime() { + return totalTime; + } + public static class Builder { private final String name; private final VeraApp app; From b22aa87303419e7556503652cc3b21a6b34b7f08 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 22:17:19 +0200 Subject: [PATCH 326/661] composite now takes in as argument for apply --- .../java/net/snackbag/vera/style/animation/VeraPipeline.java | 2 +- .../vera/style/animation/composite/AnimationComposite.java | 4 ++-- .../snackbag/vera/style/animation/composite/Composite.java | 2 +- .../vera/style/animation/composite/DiscardComposite.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java index d66213ac..a6e2f885 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java +++ b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java @@ -42,7 +42,7 @@ public T applyComposites(AnimationEngine engine, String style, StyleValueTyp isNewFrame = true; } - in = pass.apply(ctx, isNewFrame); + in = pass.apply(ctx, in, isNewFrame); } return in; diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index a8181347..1b68b0e0 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -10,7 +10,7 @@ public class AnimationComposite extends Composite { private final HashMap> precalculatedStyles = new HashMap<>(); @Override - public T apply(Context ctx, boolean isNewFrame) { + public T apply(Context ctx, T in, boolean isNewFrame) { AnimationEngine engine = ctx.engine(); if (isNewFrame) { @@ -18,7 +18,7 @@ public T apply(Context ctx, boolean isNewFrame) { precalculatedStyles.clear(); } - T out = ctx.in(); + T out = in; VAnimation[] localAnimations = animations.get(engine); // If no animations active, return the input diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index f8dccb2f..1a8a7c86 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -13,7 +13,7 @@ public abstract class Composite { * This method is called per-frame and is entirely independent of the given style. Hence, the name uniform. */ public void generateUniforms() {} - public abstract T apply(Context ctx, boolean isNewFrame); + public abstract T apply(Context ctx, T in, boolean isNewFrame); public record Context(AnimationEngine engine, String style, StyleValueType type, T in) { } diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java index 09a76a70..2bc9af70 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java @@ -2,7 +2,7 @@ public class DiscardComposite extends Composite { @Override - public T apply(Context ctx, boolean isNewFrame) { - return ctx.in(); + public T apply(Context ctx, T in, boolean isNewFrame) { + return in; } } From e5cf0e30fd3cb36cb0277c4151384a518a470716 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 22:17:24 +0200 Subject: [PATCH 327/661] remove in from ctx --- .../net/snackbag/vera/style/animation/composite/Composite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 1a8a7c86..28ef7bae 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -15,6 +15,6 @@ public abstract class Composite { public void generateUniforms() {} public abstract T apply(Context ctx, T in, boolean isNewFrame); - public record Context(AnimationEngine engine, String style, StyleValueType type, T in) { + public record Context(AnimationEngine engine, String style, StyleValueType type, T original) { } } From 751f00e7961be24bb75b9c0d1f846e89c7c45006 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 20 Jul 2025 22:47:24 +0200 Subject: [PATCH 328/661] actually define pipeline Once --- .../net/snackbag/vera/style/animation/composite/Composite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 28ef7bae..57b82ab5 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -6,7 +6,7 @@ import net.snackbag.vera.util.Once; public abstract class Composite { - public Once pipeline; + public Once pipeline = new Once<>(); public long frameTime = 0; /** From 3b3e1cc2316a4022253b51683996605cfd76dfa2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 15:57:55 +0200 Subject: [PATCH 329/661] add apply for integers --- .../net/snackbag/vera/style/animation/easing/VEasing.java | 1 + .../snackbag/vera/style/animation/easing/VLinearEasing.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java index 638cfe77..679f6ebe 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java @@ -2,4 +2,5 @@ public abstract class VEasing { public abstract float apply(float from, float to, float delta); + public abstract int apply(int from, int to, float delta); } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java index bf43d734..c1a58818 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java @@ -7,4 +7,10 @@ protected VLinearEasing() {} public float apply(float from, float to, float delta) { return from + delta * (to - from); } + + @Override + public int apply(int from, int to, float delta) { + final float fromF = (float) from; + return Math.round(fromF + delta * ((float) to - fromF)); + } } From f6f8439fd5b6845a6f08bd2d4b4e6f951023c7cb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 15:58:16 +0200 Subject: [PATCH 330/661] crash fix --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 57bd0320..b33c42bf 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -54,6 +54,7 @@ public void checkCache() { public T animateStyle(String style, T value) { checkCache(); + if (value == null) return null; if (cache.containsKey(style)) return (T) cache.get(style); StyleValueType type = StyleValueType.get(value, null); From 6380108977cb0d2049bfd8c318b6fd53e5dd7b47 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 15:58:28 +0200 Subject: [PATCH 331/661] test animations --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 869885e6..a506c405 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -7,6 +7,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -30,6 +31,15 @@ public void init() { testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); + + new VShortcut(this, "a", () -> { + testRect.animations.activate( + new VAnimation.Builder(this, "test") + .keyframe(1000, frame -> frame.style("background-color", VColor.MC_GOLD), 2000) + .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) + .build() + ); + }).alsoAdd(); } public VStyleSheet createStyleSheet() { From 8e6af80e39e47d4fc94a4e1974751de2943411f5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 16:12:30 +0200 Subject: [PATCH 332/661] net.snackbag.core.V4x moved into separate v4 package --- src/main/java/net/snackbag/vera/core/{ => v4}/V4Color.java | 4 +++- src/main/java/net/snackbag/vera/core/{ => v4}/V4Int.java | 2 +- src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java | 2 +- src/main/java/net/snackbag/vera/style/StyleValueType.java | 2 ++ .../snackbag/vera/style/standard/DropdownStandardStyle.java | 2 +- .../net/snackbag/vera/style/standard/LabelStandardStyle.java | 2 +- .../snackbag/vera/style/standard/LineInputStandardStyle.java | 2 +- .../net/snackbag/vera/style/standard/WidgetStandardStyle.java | 4 ++-- src/main/java/net/snackbag/vera/widget/VDropdown.java | 1 + src/main/java/net/snackbag/vera/widget/VLabel.java | 1 + src/main/java/net/snackbag/vera/widget/VLineInput.java | 1 + src/main/java/net/snackbag/vera/widget/VWidget.java | 2 ++ 12 files changed, 17 insertions(+), 8 deletions(-) rename src/main/java/net/snackbag/vera/core/{ => v4}/V4Color.java (93%) rename src/main/java/net/snackbag/vera/core/{ => v4}/V4Int.java (96%) diff --git a/src/main/java/net/snackbag/vera/core/V4Color.java b/src/main/java/net/snackbag/vera/core/v4/V4Color.java similarity index 93% rename from src/main/java/net/snackbag/vera/core/V4Color.java rename to src/main/java/net/snackbag/vera/core/v4/V4Color.java index ff12a1d9..e7eadca2 100644 --- a/src/main/java/net/snackbag/vera/core/V4Color.java +++ b/src/main/java/net/snackbag/vera/core/v4/V4Color.java @@ -1,4 +1,6 @@ -package net.snackbag.vera.core; +package net.snackbag.vera.core.v4; + +import net.snackbag.vera.core.VColor; public class V4Color { private final VColor v1; diff --git a/src/main/java/net/snackbag/vera/core/V4Int.java b/src/main/java/net/snackbag/vera/core/v4/V4Int.java similarity index 96% rename from src/main/java/net/snackbag/vera/core/V4Int.java rename to src/main/java/net/snackbag/vera/core/v4/V4Int.java index 44e64507..cfc42113 100644 --- a/src/main/java/net/snackbag/vera/core/V4Int.java +++ b/src/main/java/net/snackbag/vera/core/v4/V4Int.java @@ -1,4 +1,4 @@ -package net.snackbag.vera.core; +package net.snackbag.vera.core.v4; public class V4Int { private final int v1; diff --git a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java index 432e7a59..266cac8e 100644 --- a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java +++ b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java @@ -1,6 +1,6 @@ package net.snackbag.vera.modifier; -import net.snackbag.vera.core.V4Int; +import net.snackbag.vera.core.v4.V4Int; import org.jetbrains.annotations.ApiStatus; @ApiStatus.ScheduledForRemoval(inVersion = "1.11") diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index ebefafae..72f2b387 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -3,6 +3,8 @@ import net.minecraft.util.Identifier; import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.core.*; +import net.snackbag.vera.core.v4.V4Color; +import net.snackbag.vera.core.v4.V4Int; import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java index 5951d93e..8750e490 100644 --- a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java @@ -1,6 +1,6 @@ package net.snackbag.vera.style.standard; -import net.snackbag.vera.core.V4Int; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; diff --git a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java index 070a4596..8a441a8d 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java @@ -1,6 +1,6 @@ package net.snackbag.vera.style.standard; -import net.snackbag.vera.core.V4Int; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.style.StyleValueType; diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java index 9b622d4b..ca4fba13 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -1,6 +1,6 @@ package net.snackbag.vera.style.standard; -import net.snackbag.vera.core.V4Int; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index f430e3a8..7cded930 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -1,7 +1,7 @@ package net.snackbag.vera.style.standard; -import net.snackbag.vera.core.V4Color; -import net.snackbag.vera.core.V4Int; +import net.snackbag.vera.core.v4.V4Color; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.style.StyleValueType; diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index a6bfd5c4..77893bbd 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -3,6 +3,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 962b7a3e..c2c7a6fd 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -2,6 +2,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 262986a8..957a7a53 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -4,6 +4,7 @@ import net.minecraft.client.util.InputUtil; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.event.VCharLimitedEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.modifier.VHasPlaceholderFont; diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 3d13ba43..c8dcf448 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -3,6 +3,8 @@ import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; +import net.snackbag.vera.core.v4.V4Color; +import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.event.*; import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; From 96022c2b47fd9e85e1c248da05613bce66b283eb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 16:13:06 +0200 Subject: [PATCH 333/661] add animation transition --- .../snackbag/vera/style/StyleValueType.java | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 72f2b387..6d36eb6a 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -5,26 +5,42 @@ import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Color; import net.snackbag.vera.core.v4.V4Int; +import net.snackbag.vera.style.animation.easing.VEasing; import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; public enum StyleValueType { - STRING(""), - IDENTIFIER(Identifier.of(MinecraftVera.MOD_ID, "empty")), - INT(0), - FLOAT(0.0F), + STRING("", (f, t, e, d) -> d > 0.5 ? t : f), + IDENTIFIER(Identifier.of(MinecraftVera.MOD_ID, "empty"), (f, t, e, d) -> d > 0.5 ? t : f), + INT(0, (from, to, easing, delta) -> easing.apply(from, to, delta)), + FLOAT(0.0F, (from, to, easing, delta) -> easing.apply(from, to, delta)), - COLOR(VColor.black()), - FONT(VFont.create()), - CURSOR(VCursorShape.DEFAULT), + COLOR(VColor.black(), (from, to, easing, delta) -> from.ease(easing, to, delta)), + FONT(VFont.create(), (from, to, easing, delta) -> + VFont.create().withColor(from.getColor().ease(easing, to.getColor(), delta)) + .withSize(easing.apply(from.getSize(), to.getSize(), delta)) + .withName(delta > 0.5 ? to.getName() : from.getName())), + CURSOR(VCursorShape.DEFAULT, (f, t, e, d) -> d > 0.5 ? t : f), - V4INT(new V4Int(0)), - V4COLOR(new V4Color(VColor.black())); + V4INT(new V4Int(0), (from, to, easing, delta) -> new V4Int( + easing.apply(from.get1(), to.get1(), delta), + easing.apply(from.get2(), to.get2(), delta), + easing.apply(from.get3(), to.get3(), delta), + easing.apply(from.get4(), to.get4(), delta) + )), + V4COLOR(new V4Color(VColor.black()), (from, to, easing, delta) -> new V4Color( + from.get1().ease(easing, to.get1(), delta), + from.get2().ease(easing, to.get2(), delta), + from.get3().ease(easing, to.get3(), delta), + from.get4().ease(easing, to.get4(), delta) + )); public final Object standard; + public final EaseContext animationTransition; - StyleValueType(Object standard) { + StyleValueType(T standard, EaseContext animationTransition) { this.standard = standard; + this.animationTransition = (EaseContext) animationTransition; } public static StyleValueType get(Object val, @Nullable StyleValueType bias) { @@ -78,4 +94,9 @@ else if (value instanceof VColor[] v) { else if (to == FLOAT && value instanceof Double v) return v.floatValue(); return value; } + + @FunctionalInterface + public interface EaseContext { + T apply(T in, T out, VEasing easing, float delta); + } } From 4364a04ef0874ca78557ad5a3eaeb3ef1b2352a4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 16:13:10 +0200 Subject: [PATCH 334/661] reformat --- .../net/snackbag/vera/style/StyleValueType.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 6d36eb6a..b615f595 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -48,10 +48,10 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { if (bias == IDENTIFIER && s.matches("^[\\w-./]*:[\\w-./]*$")) return IDENTIFIER; else if (bias == CURSOR && EnumUtils.getEnumIgnoreCase(VCursorShape.class, s) != null) return CURSOR; return STRING; - } - - else if (val instanceof V4Color || (bias == V4COLOR && (val instanceof VColor[] || val instanceof VColor))) return V4COLOR; - else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val instanceof Integer[] || val instanceof Integer))) return V4INT; + } else if (val instanceof V4Color || (bias == V4COLOR && (val instanceof VColor[] || val instanceof VColor))) + return V4COLOR; + else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val instanceof Integer[] || val instanceof Integer))) + return V4INT; else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof Integer) return INT; @@ -74,7 +74,8 @@ else if (value instanceof int[] || value instanceof Integer[]) { case 1 -> new V4Int(v[0]); case 2 -> new V4Int(v[0], v[1]); case 4 -> new V4Int(v[0], v[1], v[2], v[3]); - default -> throw new RuntimeException("invalid V4Int format. Length must be 1, 2 or 4. Provided: %d".formatted(v.length)); + default -> + throw new RuntimeException("invalid V4Int format. Length must be 1, 2 or 4. Provided: %d".formatted(v.length)); }; } @@ -85,7 +86,8 @@ else if (value instanceof VColor[] v) { case 1 -> new V4Color(v[0]); case 2 -> new V4Color(v[0], v[1]); case 4 -> new V4Color(v[0], v[1], v[2], v[3]); - default -> throw new RuntimeException("invalid V4Color format. Length must be 1, 2 or 4. Provided: %d".formatted(v.length)); + default -> + throw new RuntimeException("invalid V4Color format. Length must be 1, 2 or 4. Provided: %d".formatted(v.length)); }; } From 384cf8b508266761e0ca19872d52ab77e92635c1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 16:13:20 +0200 Subject: [PATCH 335/661] actually set the animation --- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 1d370a5f..7f06ffa1 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -40,6 +40,7 @@ public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode lo public void addKeyframe(VKeyframe keyframe) { this.keyframes.add(keyframe); + keyframe.animation.set(this); for (String key : keyframe.styles.keySet()) { styleAffections.merge(key, 1, Integer::sum); From fcf0ae70feb6e8966a21489c7a5ffd334750a8c5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 16:14:17 +0200 Subject: [PATCH 336/661] imports --- src/main/java/net/snackbag/vera/core/VColor.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index d3b2da74..5cb59255 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -1,5 +1,7 @@ package net.snackbag.vera.core; +import net.snackbag.vera.style.animation.easing.VEasing; + import java.util.function.Consumer; public class VColor { @@ -156,6 +158,15 @@ public VColor sub(int red, int green, int blue) { return new VColor(Math.max(this.red - red, 0), Math.max(this.green - green, 0), Math.max(this.blue - blue, 0)); } + public VColor ease(VEasing easing, VColor target, float delta) { + return new VColor( + easing.apply(red, target.red, delta), + easing.apply(green, target.green, delta), + easing.apply(blue, target.blue, delta), + easing.apply(opacity, target.opacity, delta) + ); + } + public static VColor transparent() { return new VColor(0, 0, 0, 0); } From 78ddcc898f09407e99a3af0a1bdd831c410bc8cc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 16:14:31 +0200 Subject: [PATCH 337/661] also animate style with other getStyle method --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index c8dcf448..5607a5f3 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -67,7 +67,7 @@ public V getStyle(String key) { } public V getStyle(String key, StyleState state) { - return app.styleSheet.getKey(this, key, state); + return animations.animateStyle(key, app.styleSheet.getKey(this, key, state)); } public V getStyleOrDefault(String key, V dflt) { From 1217e148ef798c13cdbb178a09f02e37ec1d29d7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:07:08 +0200 Subject: [PATCH 338/661] fix keyframe styles no longer correct --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 5 ----- .../java/net/snackbag/vera/style/animation/VKeyframe.java | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 7f06ffa1..0e2051d5 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -41,11 +41,6 @@ public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode lo public void addKeyframe(VKeyframe keyframe) { this.keyframes.add(keyframe); keyframe.animation.set(this); - - for (String key : keyframe.styles.keySet()) { - styleAffections.merge(key, 1, Integer::sum); - } - totalTime += keyframe.cumulatedTime; } diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index e456eb24..8cc182ac 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -28,6 +28,8 @@ public void style(String key, Object value) { StyleValueType reservation = animation.get().app.styleSheet.getReservation(key); if (reservation == null) throw new UnsupportedOperationException("Cannot set keyframe style to unreserved style key"); + animation.get().styleAffections.merge(key, 1, Integer::sum); + Object converted = StyleValueType.convert(value, reservation); styles.put(key, new Pair<>(reservation, converted)); } From 4587e7769788ac2328e3d4fb71e32641e2f26efb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:07:16 +0200 Subject: [PATCH 339/661] make protected --- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 0e2051d5..4cfaaa4e 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -25,7 +25,7 @@ public class VAnimation { public final LoopMode loopMode; private final List keyframes = new ArrayList<>(); - private final HashMap styleAffections = new HashMap<>(); + protected final HashMap styleAffections = new HashMap<>(); private int totalTime = 0; From 21a437172f072c5706953086da5c4bf33ccfe77f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:07:35 +0200 Subject: [PATCH 340/661] fix builder --- .../snackbag/vera/style/animation/VAnimation.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 4cfaaa4e..c632c040 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -60,7 +60,7 @@ public static class Builder { private int unwindTime = 0; private VEasing unwindEasing = Easings.LINEAR; - private final List keyframes = new ArrayList<>(); + private final List>> keyframes = new ArrayList<>(); public Builder(VeraApp app, String name) { this.name = name; @@ -83,18 +83,16 @@ public Builder unwindEasing(VEasing easing) { } public Builder keyframe(int transitionMs, Consumer frame, int stayMs) { - VKeyframe target = new VKeyframe(transitionMs, stayMs); - frame.accept(target); - - keyframes.add(target); + keyframes.add(new Pair<>(new VKeyframe(transitionMs, stayMs), frame)); return this; } public VAnimation build() { VAnimation animation = new VAnimation(name, unwindTime, unwindEasing, loopMode, app); - for (VKeyframe frame : keyframes) { - animation.addKeyframe(frame); + for (Pair> frame : keyframes) { + animation.addKeyframe(frame.getA()); + frame.getB().accept(frame.getA()); } return animation; From 2224d9ce65096c422197f05025fc246a9830476f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:07:48 +0200 Subject: [PATCH 341/661] basic calculateStyle method --- .../vera/style/animation/VAnimation.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index c632c040..c163bd83 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -1,8 +1,11 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.style.animation.easing.VEasing; +import org.jetbrains.annotations.Nullable; +import oshi.util.tuples.Pair; import java.util.ArrayList; import java.util.HashMap; @@ -48,6 +51,50 @@ public boolean affects(String style) { return styleAffections.containsKey(style); } + public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long ms) { + // TODO: implement loop modes + + if (!affects(style)) { + return null; + } + + int margin = 0; + for (int i = 0; i < keyframes.size(); i++) { + VKeyframe frame = keyframes.get(i); + + if (!(ms > margin && ms < margin + frame.cumulatedTime)) { + margin += frame.cumulatedTime; + continue; + } + + int time = (int) ms - margin; + boolean isTransition = time <= frame.transitionTime; + + if (!isTransition) return (T) frame.styles.get(style).getB(); + + T before; + T after = getStyleForKeyframeDeep(i, style); + + if (i > 0) before = getStyleForKeyframeDeep(i - 1, style); + else before = original; + + return (T) svt.animationTransition.apply(before, after, frame.easeIn, ms / (float) frame.transitionTime); + } + + return original; + } + + public T getStyleForKeyframeDeep(int targetIndex, String style) { + VKeyframe target = keyframes.get(targetIndex); + + for (int i = targetIndex; !target.styles.containsKey(style); i--) { + target = keyframes.get(i - 1); + } + + Pair pair = target.styles.get(style); + return (T) StyleValueType.convert(pair.getB(), pair.getA()); + } + public int getTotalTime() { return totalTime; } From fa8bb335f0da2d73414d9768433be2b380cb7f4d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:28:09 +0200 Subject: [PATCH 342/661] fix margin --- .../net/snackbag/vera/style/animation/VAnimation.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index c163bd83..8a0eb7fe 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -62,13 +62,9 @@ public boolean affects(String style) { for (int i = 0; i < keyframes.size(); i++) { VKeyframe frame = keyframes.get(i); - if (!(ms > margin && ms < margin + frame.cumulatedTime)) { - margin += frame.cumulatedTime; - continue; - } - - int time = (int) ms - margin; - boolean isTransition = time <= frame.transitionTime; + if (ms >= margin && ms <= margin + frame.cumulatedTime) { + int timeInFrame = (int) ms - margin; + boolean isTransition = timeInFrame <= frame.transitionTime; if (!isTransition) return (T) frame.styles.get(style).getB(); @@ -79,6 +75,7 @@ public boolean affects(String style) { else before = original; return (T) svt.animationTransition.apply(before, after, frame.easeIn, ms / (float) frame.transitionTime); + margin += frame.cumulatedTime; } return original; From 61d3a7bf4ec6d599ab1bf91243f812f29acc22c0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:28:20 +0200 Subject: [PATCH 343/661] fix indent --- .../vera/style/animation/VAnimation.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 8a0eb7fe..1e454f8b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -66,15 +66,19 @@ public boolean affects(String style) { int timeInFrame = (int) ms - margin; boolean isTransition = timeInFrame <= frame.transitionTime; - if (!isTransition) return (T) frame.styles.get(style).getB(); + if (!isTransition) return (T) frame.styles.get(style).getB(); - T before; - T after = getStyleForKeyframeDeep(i, style); + T before; + T after = getStyleForKeyframeDeep(i, style); - if (i > 0) before = getStyleForKeyframeDeep(i - 1, style); - else before = original; + if (i > 0) before = getStyleForKeyframeDeep(i - 1, style); + else before = original; + + float progress = timeInFrame / (float) frame.transitionTime; + + return (T) svt.animationTransition.apply(before, after, frame.easeIn, progress); + } - return (T) svt.animationTransition.apply(before, after, frame.easeIn, ms / (float) frame.transitionTime); margin += frame.cumulatedTime; } From 91ee4662cb2ce43035e810eb8f6b53440369ea1e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:31:00 +0200 Subject: [PATCH 344/661] applyWidget & apply -> applyStyle + no more abstract --- .../vera/style/animation/composite/AnimationComposite.java | 2 +- .../snackbag/vera/style/animation/composite/Composite.java | 7 ++++++- .../vera/style/animation/composite/DiscardComposite.java | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 1b68b0e0..1871267d 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -10,7 +10,7 @@ public class AnimationComposite extends Composite { private final HashMap> precalculatedStyles = new HashMap<>(); @Override - public T apply(Context ctx, T in, boolean isNewFrame) { + public T applyStyle(Context ctx, T in, boolean isNewFrame) { AnimationEngine engine = ctx.engine(); if (isNewFrame) { diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index 57b82ab5..c32967e9 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -13,7 +13,12 @@ public abstract class Composite { * This method is called per-frame and is entirely independent of the given style. Hence, the name uniform. */ public void generateUniforms() {} - public abstract T apply(Context ctx, T in, boolean isNewFrame); + + public void applyWidget(VWidget widget) {} + + public T applyStyle(Context ctx, T in, boolean isNewFrame) { + return in; + } public record Context(AnimationEngine engine, String style, StyleValueType type, T original) { } diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java index 2bc9af70..b3321241 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java @@ -2,7 +2,7 @@ public class DiscardComposite extends Composite { @Override - public T apply(Context ctx, T in, boolean isNewFrame) { + public T applyStyle(Context ctx, T in, boolean isNewFrame) { return in; } } From c3f1dd9c45a55c0a8066233dc04e3703f88b337d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:31:24 +0200 Subject: [PATCH 345/661] imports --- .../net/snackbag/vera/style/animation/composite/Composite.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java index c32967e9..160ed1b4 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java @@ -4,6 +4,7 @@ import net.snackbag.vera.style.animation.AnimationEngine; import net.snackbag.vera.style.animation.VeraPipeline; import net.snackbag.vera.util.Once; +import net.snackbag.vera.widget.VWidget; public abstract class Composite { public Once pipeline = new Once<>(); From 38d0562a8a7188b148ac6ead63690f3fef74f923 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:31:31 +0200 Subject: [PATCH 346/661] fix name --- .../java/net/snackbag/vera/style/animation/VeraPipeline.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java index a6e2f885..5e41cdf6 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java +++ b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java @@ -42,7 +42,7 @@ public T applyComposites(AnimationEngine engine, String style, StyleValueTyp isNewFrame = true; } - in = pass.apply(ctx, in, isNewFrame); + in = pass.applyStyle(ctx, in, isNewFrame); } return in; From 284f7354ef05a3995ac24329a930e69c45166917 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:35:05 +0200 Subject: [PATCH 347/661] simpler AnimationComposite --- .../composite/AnimationComposite.java | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 1871267d..a71a3d5d 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -2,6 +2,7 @@ import net.snackbag.vera.style.animation.AnimationEngine; import net.snackbag.vera.style.animation.VAnimation; +import net.snackbag.vera.widget.VWidget; import java.util.HashMap; @@ -9,42 +10,22 @@ public class AnimationComposite extends Composite { private final HashMap animations = new HashMap<>(); private final HashMap> precalculatedStyles = new HashMap<>(); + @Override + public void applyWidget(VWidget widget) { + animations.put(widget.animations, widget.animations.getAllActive()); + } + @Override public T applyStyle(Context ctx, T in, boolean isNewFrame) { - AnimationEngine engine = ctx.engine(); + // No caching necessary, since this is done earlier. - if (isNewFrame) { - animations.put(engine, engine.getAllActive()); - precalculatedStyles.clear(); - } + AnimationEngine engine = ctx.engine(); T out = in; - VAnimation[] localAnimations = animations.get(engine); - - // If no animations active, return the input - if (localAnimations == null) return out; - - String style = ctx.style(); - - for (VAnimation animation : localAnimations) { - if (!animation.affects(style)) continue; - - boolean animationCached = precalculatedStyles.containsKey(animation); - - // If animation isn't cached or the key isn't cached - if (!animationCached || !precalculatedStyles.get(animation).containsKey(style)) { - if (!animationCached) precalculatedStyles.put(animation, new HashMap<>()); - - precalculatedStyles.get(animation).put( - style, - animation.calculateStyle( - style, - System.currentTimeMillis() - engine.getTimeSinceActive(animation) - ) - ); - } - - out = (T) precalculatedStyles.get(animation).get(style); + for (VAnimation animation : animations.get(engine)) { + // No need to check affects, since calculateStyle does this internally + T rv = animation.calculateStyle(ctx.style(), ctx.original(), ctx.type(), System.currentTimeMillis() - engine.getTimeSinceActive(animation)); + if (rv != null) out = rv; } return out; From 7a498b9942dba65b00ee166b6c7ba19bd275ce70 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:35:19 +0200 Subject: [PATCH 348/661] remove redundant variable --- .../vera/style/animation/composite/AnimationComposite.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index a71a3d5d..0001d6b1 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -8,7 +8,6 @@ public class AnimationComposite extends Composite { private final HashMap animations = new HashMap<>(); - private final HashMap> precalculatedStyles = new HashMap<>(); @Override public void applyWidget(VWidget widget) { From 3b21bc88e0883e6717ad9e0cc4d54e2eb4640f51 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:35:36 +0200 Subject: [PATCH 349/661] use generateUniforms to clear animations --- .../vera/style/animation/composite/AnimationComposite.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 0001d6b1..9c2b0cd1 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -9,6 +9,11 @@ public class AnimationComposite extends Composite { private final HashMap animations = new HashMap<>(); + @Override + public void generateUniforms() { + animations.clear(); + } + @Override public void applyWidget(VWidget widget) { animations.put(widget.animations, widget.animations.getAllActive()); From 6c89559b05c72d3539f76b843fcb635e48cbba6a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:40:44 +0200 Subject: [PATCH 350/661] cache animation times --- .../composite/AnimationComposite.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 9c2b0cd1..77f7b4ba 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -8,27 +8,40 @@ public class AnimationComposite extends Composite { private final HashMap animations = new HashMap<>(); + private final HashMap animationTimes = new HashMap<>(); @Override public void generateUniforms() { animations.clear(); + animationTimes.clear(); } @Override public void applyWidget(VWidget widget) { - animations.put(widget.animations, widget.animations.getAllActive()); + VAnimation[] active = widget.animations.getAllActive(); + animations.put(widget.animations, active); + + for (VAnimation animation : active) { + animationTimes.put(animation, System.currentTimeMillis() - widget.animations.getTimeSinceActive(animation)); + } } @Override public T applyStyle(Context ctx, T in, boolean isNewFrame) { - // No caching necessary, since this is done earlier. + // Most caching isn't necessary, since it's done earlier. AnimationEngine engine = ctx.engine(); T out = in; for (VAnimation animation : animations.get(engine)) { // No need to check affects, since calculateStyle does this internally - T rv = animation.calculateStyle(ctx.style(), ctx.original(), ctx.type(), System.currentTimeMillis() - engine.getTimeSinceActive(animation)); + T rv = animation.calculateStyle( + ctx.style(), + ctx.original(), + ctx.type(), + animationTimes.get(animation) + ); + if (rv != null) out = rv; } From b1e71c569a56ca30b2636c90b30a9584b431bb9e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:46:50 +0200 Subject: [PATCH 351/661] call applyWidget --- .../java/net/snackbag/vera/style/animation/VeraPipeline.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java index 5e41cdf6..c1e42e5c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java +++ b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java @@ -4,6 +4,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.animation.composite.Composite; +import net.snackbag.vera.widget.VWidget; import java.util.ArrayList; import java.util.List; @@ -11,6 +12,7 @@ public class VeraPipeline { public final VeraApp app; private final List passes = new ArrayList<>(); + private final List> cleanWidgets = new ArrayList<>(); public VeraPipeline(VeraApp app) { this.app = app; @@ -38,10 +40,12 @@ public T applyComposites(AnimationEngine engine, String style, StyleValueTyp for (Composite pass : passes) { if (pass.frameTime != Vera.renderCacheId) { pass.frameTime = Vera.renderCacheId; + cleanWidgets.clear(); pass.generateUniforms(); isNewFrame = true; } + if (!cleanWidgets.contains(engine.widget)) pass.applyWidget(engine.widget); in = pass.applyStyle(ctx, in, isNewFrame); } From 542016cbb21320b7bb12f2e6d757143b6ead5d3a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 17:47:42 +0200 Subject: [PATCH 352/661] DiscardComposite -> UnwindComposite --- src/main/java/net/snackbag/vera/core/VeraApp.java | 4 ++-- .../vera/style/animation/composite/DiscardComposite.java | 8 -------- .../vera/style/animation/composite/UnwindComposite.java | 4 ++++ 3 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index ff5bf5df..01b3880a 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -8,7 +8,7 @@ import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.style.animation.VeraPipeline; import net.snackbag.vera.style.animation.composite.AnimationComposite; -import net.snackbag.vera.style.animation.composite.DiscardComposite; +import net.snackbag.vera.style.animation.composite.UnwindComposite; import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -64,7 +64,7 @@ public VeraApp(boolean mouseRequired) { public void loadComposites() { pipeline.addPass(new AnimationComposite()); - pipeline.addPass(new DiscardComposite()); + pipeline.addPass(new UnwindComposite()); } public void setCursorVisible(boolean cursorVisible) { diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java deleted file mode 100644 index b3321241..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/composite/DiscardComposite.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.snackbag.vera.style.animation.composite; - -public class DiscardComposite extends Composite { - @Override - public T applyStyle(Context ctx, T in, boolean isNewFrame) { - return in; - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java new file mode 100644 index 00000000..a398f0fe --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java @@ -0,0 +1,4 @@ +package net.snackbag.vera.style.animation.composite; + +public class UnwindComposite extends Composite { +} From b44e7248180af5012c892131a985908c47388ecb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 18:49:31 +0200 Subject: [PATCH 353/661] micro optimizations --- .../composite/AnimationComposite.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 77f7b4ba..4b127609 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -9,20 +9,26 @@ public class AnimationComposite extends Composite { private final HashMap animations = new HashMap<>(); private final HashMap animationTimes = new HashMap<>(); + private long time; // so we don't have to call it again @Override public void generateUniforms() { animations.clear(); animationTimes.clear(); + time = System.currentTimeMillis(); } @Override public void applyWidget(VWidget widget) { VAnimation[] active = widget.animations.getAllActive(); - animations.put(widget.animations, active); - for (VAnimation animation : active) { - animationTimes.put(animation, System.currentTimeMillis() - widget.animations.getTimeSinceActive(animation)); + // Only store if we have active animations + if (active.length > 0) { + animations.put(widget.animations, active); + + for (VAnimation animation : active) { + animationTimes.put(animation, time - widget.animations.getTimeSinceActive(animation)); + } } } @@ -31,9 +37,13 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { // Most caching isn't necessary, since it's done earlier. AnimationEngine engine = ctx.engine(); + VAnimation[] engineAnimations = animations.get(engine); + + // early return + if (engineAnimations == null || engineAnimations.length == 0) return in; T out = in; - for (VAnimation animation : animations.get(engine)) { + for (VAnimation animation : engineAnimations) { // No need to check affects, since calculateStyle does this internally T rv = animation.calculateStyle( ctx.style(), From 8730bea474bdbb05bc499ff50076313c37eacdba Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 18:49:41 +0200 Subject: [PATCH 354/661] correct active check --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index b33c42bf..145870de 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -32,7 +32,7 @@ public void activate(VAnimation animation) { } public void activate(VAnimation animation, boolean override) { - if (!override && activeAnimations.containsKey(animation)) return; + if (!override && isActive(animation.name)) return; activeAnimations.put(animation, System.currentTimeMillis()); } From 360e82081adacaf193b8bd7624406c78c13c14ad Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 18:53:57 +0200 Subject: [PATCH 355/661] getIfActive -> getIfEverActive --- .../vera/style/animation/AnimationEngine.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 145870de..717888ee 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -15,6 +15,7 @@ public class AnimationEngine { public final VWidget widget; private final HashMap activeAnimations = new HashMap<>(); + private final HashMap animationHistory = new HashMap<>(); private long cacheId = 0; private final HashMap cache = new HashMap<>(); @@ -27,6 +28,10 @@ public boolean isActive(String name) { return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } + public boolean isUnwinding(String name) { + + } + public void activate(VAnimation animation) { activate(animation, false); } @@ -36,8 +41,19 @@ public void activate(VAnimation animation, boolean override) { activeAnimations.put(animation, System.currentTimeMillis()); } - public @Nullable VAnimation getIfActive(String name) { - return activeAnimations.keySet().stream().filter(anim -> anim.name.equals(name)).findFirst().orElse(null); + public @Nullable VAnimation getIfEverActive(String name) { + return activeAnimations.keySet() + .stream() + .filter(anim -> anim.name.equals(name)) + .findFirst() + + .orElse( + animationHistory.keySet() + .stream() + .filter(anim -> anim.name.equals(name)) + .findFirst() + .orElse(null) + ); } public VAnimation[] getAllActive() { From ffbe52f9cbd584ad787cd63a7b5d488e273585ea Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 21 Jul 2025 18:58:56 +0200 Subject: [PATCH 356/661] add setSize method --- src/main/java/net/snackbag/vera/core/VeraApp.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 01b3880a..a8b1f196 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -154,6 +154,15 @@ public void setWidth(int width) { this.width = width; } + public void setSize(int both) { + setSize(both, both); + } + + public void setSize(int width, int height) { + setWidth(width); + setHeight(height); + } + public void move(int x, int y) { this.x = x; this.y = y; From 5291dec726f6f74ef40accdc4a3390d939041fd2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 09:40:18 +0200 Subject: [PATCH 357/661] undo unwinding --- .../vera/style/animation/AnimationEngine.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 717888ee..9122474b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -1,12 +1,12 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import java.util.HashMap; -import java.util.LinkedHashSet; /** * Per-widget handler for animations. This is after stylesheet.getKey, so there is no differentiation between @@ -15,7 +15,6 @@ public class AnimationEngine { public final VWidget widget; private final HashMap activeAnimations = new HashMap<>(); - private final HashMap animationHistory = new HashMap<>(); private long cacheId = 0; private final HashMap cache = new HashMap<>(); @@ -28,8 +27,11 @@ public boolean isActive(String name) { return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } - public boolean isUnwinding(String name) { - + /** + * Moves animations from {@link #activeAnimations} to {@link #animationHistory} and back dependent on their state. + * Called in {@link net.snackbag.mcvera.impl.MCVeraRenderer#renderApp(VeraApp)} + */ + public void update() { } public void activate(VAnimation animation) { @@ -46,14 +48,7 @@ public void activate(VAnimation animation, boolean override) { .stream() .filter(anim -> anim.name.equals(name)) .findFirst() - - .orElse( - animationHistory.keySet() - .stream() - .filter(anim -> anim.name.equals(name)) - .findFirst() - .orElse(null) - ); + .orElse(null); } public VAnimation[] getAllActive() { From c256e53aa634e11ee6345f84ee022c67dfae650c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 09:40:25 +0200 Subject: [PATCH 358/661] call update --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 2f7bfc74..5859dd22 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -74,6 +74,8 @@ public void renderApp(VeraApp app) { if (widget != hoveredWidget && widget.isHovered()) widget.setHovered(false); else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); + widget.animations.update(); + if (widget.visibilityConditionsPassed()) { widget.render(); widget.renderBorder(); From 939e051302e263193c455df6e38ede01bec8c46c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 09:40:36 +0200 Subject: [PATCH 359/661] equals and hashCode --- .../snackbag/vera/style/animation/VAnimation.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 1e454f8b..b014c140 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -100,6 +100,18 @@ public int getTotalTime() { return totalTime; } + @Override + public boolean equals(Object o) { + if (!(o instanceof VAnimation animation)) return false; + return Objects.equals(name, animation.name) && Objects.equals(app, animation.app); + } + + @Override + public int hashCode() { + return Objects.hash(name, app); + } + + public static class Builder { private final String name; private final VeraApp app; From 378aaf69d61c9657bc34030641b9beeee8891345 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 09:40:53 +0200 Subject: [PATCH 360/661] getKeyframes --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index b014c140..bd567fd2 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; /** @@ -111,6 +112,9 @@ public int hashCode() { return Objects.hash(name, app); } + public VKeyframe[] getKeyframes() { + return keyframes.toArray(new VKeyframe[0]); + } public static class Builder { private final String name; From 9ea8423ce7313274326841a6b15b2d832d47bad9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 10:56:44 +0200 Subject: [PATCH 361/661] totalTime now contains unwindTime --- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index bd567fd2..0098f57a 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -37,6 +37,7 @@ public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode lo this.name = name; this.unwindTime = unwindTime; this.unwindEasing = unwindEasing; + this.totalTime = unwindTime; this.loopMode = loopMode; this.app = app; From c390311867ea7d105d8a279113b7d11fd6b48f5e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 10:56:56 +0200 Subject: [PATCH 362/661] ms -> timeSinceActive --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 0098f57a..e80b8539 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -53,7 +53,7 @@ public boolean affects(String style) { return styleAffections.containsKey(style); } - public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long ms) { + public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long timeSinceActive) { // TODO: implement loop modes if (!affects(style)) { @@ -64,8 +64,8 @@ public boolean affects(String style) { for (int i = 0; i < keyframes.size(); i++) { VKeyframe frame = keyframes.get(i); - if (ms >= margin && ms <= margin + frame.cumulatedTime) { - int timeInFrame = (int) ms - margin; + if (timeSinceActive >= margin && timeSinceActive <= margin + frame.cumulatedTime) { + int timeInFrame = (int) timeSinceActive - margin; boolean isTransition = timeInFrame <= frame.transitionTime; if (!isTransition) return (T) frame.styles.get(style).getB(); From 858a056ac4dbf801e82522fbac866f45f69347b3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 10:57:06 +0200 Subject: [PATCH 363/661] save animation --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index a506c405..b1977298 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -14,6 +14,13 @@ public class StyleTestApplication extends VeraApp { public static StyleTestApplication INSTANCE = new StyleTestApplication(); + private final VAnimation testAnimation = new VAnimation.Builder(this, "test") + .unwindTime(2000) + + .keyframe(1000, frame -> frame.style("background-color", VColor.MC_GOLD), 2000) + .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) + .build(); + @Override public void init() { new VShortcut(this, "escape", this::hide).alsoAdd(); From ad6054b3cc8523665fab2658285951537e6d36d0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 10:57:13 +0200 Subject: [PATCH 364/661] basic unwind system --- .../vera/style/animation/AnimationEngine.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 9122474b..0c3129c5 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -15,6 +15,7 @@ public class AnimationEngine { public final VWidget widget; private final HashMap activeAnimations = new HashMap<>(); + private final HashMap unwindingAnimations = new HashMap<>(); private long cacheId = 0; private final HashMap cache = new HashMap<>(); @@ -27,6 +28,10 @@ public boolean isActive(String name) { return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } + public boolean isUnwinding(String name) { + return unwindingAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); + } + /** * Moves animations from {@link #activeAnimations} to {@link #animationHistory} and back dependent on their state. * Called in {@link net.snackbag.mcvera.impl.MCVeraRenderer#renderApp(VeraApp)} @@ -43,6 +48,15 @@ public void activate(VAnimation animation, boolean override) { activeAnimations.put(animation, System.currentTimeMillis()); } + public void unwind(VAnimation animation) { + unwind(animation, false); + } + + public void unwind(VAnimation animation, boolean override) { + if (!override && isUnwinding(animation.name)) return; + unwindingAnimations.put(animation, System.currentTimeMillis()); + } + public @Nullable VAnimation getIfEverActive(String name) { return activeAnimations.keySet() .stream() @@ -55,6 +69,10 @@ public VAnimation[] getAllActive() { return activeAnimations.keySet().toArray(new VAnimation[0]); } + public VAnimation[] getAllUnwinding() { + return unwindingAnimations.keySet().toArray(new VAnimation[0]); + } + public void checkCache() { if (cacheId != Vera.renderCacheId) { cache.clear(); @@ -81,4 +99,8 @@ public T animateStyle(String style, T value) { public long getTimeSinceActive(VAnimation animation) { return activeAnimations.getOrDefault(animation, -1L); } + + public long getTimeSinceUnwinding(VAnimation animation) { + return unwindingAnimations.getOrDefault(animation, -1L); + } } From c444eca09a3f9448c82930919dee84df2771a597 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 10:57:46 +0200 Subject: [PATCH 365/661] b key now unwinds --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index b1977298..aef5710f 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -39,14 +39,8 @@ public void init() { testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); - new VShortcut(this, "a", () -> { - testRect.animations.activate( - new VAnimation.Builder(this, "test") - .keyframe(1000, frame -> frame.style("background-color", VColor.MC_GOLD), 2000) - .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) - .build() - ); - }).alsoAdd(); + new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)).alsoAdd(); + new VShortcut(this, "b", () -> testRect.animations.unwind(testAnimation)).alsoAdd(); } public VStyleSheet createStyleSheet() { From bf39df87540d879f7e1ab5f696ba382cc4da25b0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 10:57:55 +0200 Subject: [PATCH 366/661] very basic and unoptimized UnwindComposite --- .../animation/composite/UnwindComposite.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java index a398f0fe..f39fd5c8 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java @@ -1,4 +1,36 @@ package net.snackbag.vera.style.animation.composite; +import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.VAnimation; + public class UnwindComposite extends Composite { + private long time; + + @Override + public void generateUniforms() { + time = System.currentTimeMillis(); + } + + @Override + public T applyStyle(Context ctx, T in, boolean isNewFrame) { + AnimationEngine engine = ctx.engine(); + + T out = in; + + for (VAnimation animation : engine.getAllUnwinding()) { + if (!animation.affects(ctx.style())) continue; + if (animation.unwindTime <= 0) { + out = ctx.original(); + continue; + } + + long sinceUnwinding = engine.getTimeSinceUnwinding(animation); + int relativeTime = (int) (time - sinceUnwinding); + float delta = Math.min((float) relativeTime / (float) animation.unwindTime, 1f); + + out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); + } + + return out; + } } From 00b69dbd78ff52a3251901442a0e86143d16f108 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 11:06:16 +0200 Subject: [PATCH 367/661] shorten --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index e80b8539..4da14925 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -56,9 +56,7 @@ public boolean affects(String style) { public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long timeSinceActive) { // TODO: implement loop modes - if (!affects(style)) { - return null; - } + if (!affects(style)) return null; int margin = 0; for (int i = 0; i < keyframes.size(); i++) { From 4b3223621e3aa93bac77a13c40e375c1fd5ef7c1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 11:06:36 +0200 Subject: [PATCH 368/661] add monotone end --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 4da14925..80f2ab34 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -53,7 +53,7 @@ public boolean affects(String style) { return styleAffections.containsKey(style); } - public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long timeSinceActive) { + public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long timeSinceActive, boolean addMonotoneEnd) { // TODO: implement loop modes if (!affects(style)) return null; @@ -82,6 +82,10 @@ public boolean affects(String style) { margin += frame.cumulatedTime; } + if (addMonotoneEnd && timeSinceActive >= totalTime - unwindTime && timeSinceActive <= totalTime) { + return (T) keyframes.get(keyframes.size() - 1).styles.get(style).getB(); + } + return original; } From 8afd5a2d466134c1da6673e22eac470459d2b00c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 11:06:47 +0200 Subject: [PATCH 369/661] update calculateStyle arguments --- .../vera/style/animation/composite/AnimationComposite.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 4b127609..867b8fda 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -49,7 +49,8 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { ctx.style(), ctx.original(), ctx.type(), - animationTimes.get(animation) + animationTimes.get(animation), + true ); if (rv != null) out = rv; From 8b3f1d4b1467c46b2f828c53958b03aaf7039838 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:02:21 +0200 Subject: [PATCH 370/661] add QOL events class --- .../java/net/snackbag/vera/event/Events.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/event/Events.java diff --git a/src/main/java/net/snackbag/vera/event/Events.java b/src/main/java/net/snackbag/vera/event/Events.java new file mode 100644 index 00000000..e20b555e --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/Events.java @@ -0,0 +1,72 @@ +package net.snackbag.vera.event; + +// Sorted by :sparkle: the feeling that it looks nice :sparkle: +public class Events { + // Animation + public static class Animation { + public static final String BEGIN = "animation-begin"; + public static final String FINISH = "animation-finish"; + public static final String UNWIND_BEGIN = "animation-unwind-begin"; // TODO: implement + } + + // Element + public static class Element { + public static final String MESSAGE = "elem-message"; + public static final String LAYOUT_SWAP = "elem-layout-swap"; + public static final String LAYOUT_REMOVE = "elem-layout-remove"; + } + + // Widget + public static class Widget { + public static final String HOVER = "hover"; + public static final String HOVER_LEAVE = "hover-leave"; + + public static final String LEFT_CLICK = "left-click"; + public static final String LEFT_CLICK_RELEASE = "left-click-release"; + public static final String MIDDLE_CLICK = "middle-click"; + public static final String MIDDLE_CLICK_RELEASE = "middle-click-release"; + public static final String RIGHT_CLICK = "right-click"; + public static final String RIGHT_CLICK_RELEASE = "right-click-release"; + + public static final String SCROLL = "mouse-scroll"; + public static final String MOUSE_MOVE = "mouse-move"; + public static final String DRAG_LEFT_CLICK = "mouse-drag-left"; + public static final String DRAG_RIGHT_CLICK = "mouse-drag-right"; + public static final String DRAG_MIDDLE_CLICK = "mouse-drag-middle"; + + public static final String FOCUS_STATE_CHANGE = "focus-state-change"; + public static final String FILES_DROPPED = "files-dropped"; + } + + // Checkbox + public static class CheckBox { + public static final String CHECKED = "vcheckbox-checked"; + } + + // Dropdown + public static class Dropdown { + public static final String ITEM_SWITCH = "vdropdown-item-switch"; + public static final String SELECTOR_OPEN = "vdropdown-selector-open"; + public static final String SELECTOR_CLOSE = "vdropdown-selector-close"; + } + + // Line input + public static class LineInput { + public static final String CHANGE = "vline-change"; + public static final String CURSOR_MOVE = "vline-cursor-move"; + public static final String CURSOR_MOVE_LEFT = "vline-cursor-move-left"; + public static final String CURSOR_MOVE_RIGHT = "vline-cursor-move-right"; + public static final String ADD_CHAR_LIMITED = "vline-add-char-limited"; + } + + // Tabs + public static class TabWidget { + public static final String TAB_HOVER_CHANGE = "vtabwidget-tab-hover-change"; + public static final String TAB_LEFT_CLICK = "vtabwidget-tab-left-click"; + public static final String TAB_LEFT_CLICK_RELEASE = "vtabwidget-tab-left-click-release"; + public static final String TAB_MIDDLE_CLICK = "vtabwidget-tab-middle-click"; + public static final String TAB_MIDDLE_CLICK_RELEASE = "vtabwidget-tab-middle-click-release"; + public static final String TAB_RIGHT_CLICK = "vtabwidget-tab-right-click"; + public static final String TAB_RIGHT_CLICK_RELEASE = "vtabwidget-tab-right-click-release"; + } +} From d4e899d6b4539ed8952eff968d48a0a2c446c374 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:02:48 +0200 Subject: [PATCH 371/661] add animation events --- .../net/snackbag/vera/event/VAnimationBeginEvent.java | 7 +++++++ .../net/snackbag/vera/event/VAnimationFinishEvent.java | 7 +++++++ src/main/java/net/snackbag/vera/widget/VWidget.java | 9 +++++++++ 3 files changed, 23 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/event/VAnimationBeginEvent.java create mode 100644 src/main/java/net/snackbag/vera/event/VAnimationFinishEvent.java diff --git a/src/main/java/net/snackbag/vera/event/VAnimationBeginEvent.java b/src/main/java/net/snackbag/vera/event/VAnimationBeginEvent.java new file mode 100644 index 00000000..5743bba2 --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/VAnimationBeginEvent.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.event; + +import net.snackbag.vera.style.animation.VAnimation; + +public interface VAnimationBeginEvent { + void run(VAnimation animation); +} diff --git a/src/main/java/net/snackbag/vera/event/VAnimationFinishEvent.java b/src/main/java/net/snackbag/vera/event/VAnimationFinishEvent.java new file mode 100644 index 00000000..77d1886b --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/VAnimationFinishEvent.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.event; + +import net.snackbag.vera.style.animation.VAnimation; + +public interface VAnimationFinishEvent { + void run(VAnimation animation, long beginTime); +} diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 5607a5f3..178c066f 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -9,6 +9,7 @@ import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.util.DragHandler; import java.nio.file.Path; @@ -244,6 +245,14 @@ public void onFilesDropped(VFilesDroppedEvent runnable) { events.register("files-dropped", args -> runnable.run((List) args[0])); } + public void onAnimationBegin(VAnimationBeginEvent runnable) { + events.register(Events.Animation.BEGIN, args -> runnable.run((VAnimation) args[0])); + } + + public void onAnimationFinish(VAnimationFinishEvent runnable) { + events.register(Events.Animation.FINISH, args -> runnable.run((VAnimation) args[0], (long) args[1])); + } + @Override public void handleBuiltinEvent(String event, Object... args) { StyleState state = createStyleState(); From 5d31fb535b464a8c5a7d8dd377bbd95ad74c3960 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:02:56 +0200 Subject: [PATCH 372/661] move to new QOL events class --- .../net/snackbag/vera/widget/VWidget.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 178c066f..9cc89fa8 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -176,73 +176,73 @@ public boolean isHovered() { public void setHovered(boolean hovered) { // If changed if (this.hovered != hovered) { - if (hovered) events.fire("hover"); - else events.fire("hover-leave"); + if (hovered) events.fire(Events.Widget.HOVER); + else events.fire(Events.Widget.HOVER_LEAVE); } this.hovered = hovered; } public void onHover(Runnable runnable) { - events.register("hover", runnable); + events.register(Events.Widget.HOVER, runnable); } public void onHoverLeave(Runnable runnable) { - events.register("hover-leave", runnable); + events.register(Events.Widget.HOVER_LEAVE, runnable); } public void onLeftClick(Runnable runnable) { - events.register("left-click", runnable); + events.register(Events.Widget.LEFT_CLICK, runnable); } public void onLeftClickRelease(Runnable runnable) { - events.register("left-click-release", runnable); + events.register(Events.Widget.LEFT_CLICK_RELEASE, runnable); } public void onRightClick(Runnable runnable) { - events.register("right-click", runnable); + events.register(Events.Widget.RIGHT_CLICK, runnable); } public void onRightClickRelease(Runnable runnable) { - events.register("right-click-release", runnable); + events.register(Events.Widget.RIGHT_CLICK_RELEASE, runnable); } public void onMiddleClick(Runnable runnable) { - events.register("middle-click", runnable); + events.register(Events.Widget.MIDDLE_CLICK, runnable); } public void onMiddleClickRelease(Runnable runnable) { - events.register("middle-click-release", runnable); + events.register(Events.Widget.MIDDLE_CLICK_RELEASE, runnable); } public void onMouseScroll(VMouseScrollEvent runnable) { - events.register("mouse-scroll", args -> runnable.run( + events.register(Events.Widget.SCROLL, args -> runnable.run( (int) args[0], (int) args[1], (double) args[2]) ); } public void onMouseMove(VMouseMoveEvent runnable) { - events.register("mouse-move", args -> runnable.run((int) args[0], (int) args[1])); + events.register(Events.Widget.MOUSE_MOVE, args -> runnable.run((int) args[0], (int) args[1])); } public void onMouseDragLeft(VMouseDragEvent runnable) { - events.register("mouse-drag-left", args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register(Events.Widget.DRAG_LEFT_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragRight(VMouseDragEvent runnable) { - events.register("mouse-drag-right", args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register(Events.Widget.DRAG_RIGHT_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragMiddle(VMouseDragEvent runnable) { - events.register("mouse-drag-middle", args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register(Events.Widget.DRAG_MIDDLE_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onFocusStateChange(Runnable runnable) { - events.register("focus-state-change", runnable); + events.register(Events.Widget.FOCUS_STATE_CHANGE, runnable); } public void onFilesDropped(VFilesDroppedEvent runnable) { - events.register("files-dropped", args -> runnable.run((List) args[0])); + events.register(Events.Widget.FILES_DROPPED, args -> runnable.run((List) args[0])); } public void onAnimationBegin(VAnimationBeginEvent runnable) { From 439420a17ac91a5a00479dfbf5e9dbacfdd11f55 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:03:03 +0200 Subject: [PATCH 373/661] auto unwind --- .../snackbag/vera/style/animation/AnimationEngine.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 0c3129c5..384e9493 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -33,10 +33,17 @@ public boolean isUnwinding(String name) { } /** - * Moves animations from {@link #activeAnimations} to {@link #animationHistory} and back dependent on their state. + * Unwinds animations whenever they come to their end. * Called in {@link net.snackbag.mcvera.impl.MCVeraRenderer#renderApp(VeraApp)} */ public void update() { + final long time = System.currentTimeMillis(); + + for (VAnimation animation : activeAnimations.keySet()) { + if (time - getTimeSinceActive(animation) >= animation.getTotalTime() - animation.unwindTime) { + unwind(animation); + } + } } public void activate(VAnimation animation) { From f61ed857357fcb667f8a8073b91941b5d82131fa Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:07:50 +0200 Subject: [PATCH 374/661] move to new events --- .../net/snackbag/vera/widget/VTabWidget.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 59b36a4a..fdae2c66 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -4,6 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; @@ -60,37 +61,37 @@ public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); switch (event) { - case "mouse-move" -> getHoveredTabIndex((int) args[0]); + case Events.Widget.MOUSE_MOVE -> getHoveredTabIndex((int) args[0]); - case "hover-enter" -> getHoveredTabIndex(Vera.getMouseX()); - case "hover-leave" -> hoveredTab = null; + case Events.Widget.HOVER -> getHoveredTabIndex(Vera.getMouseX()); + case Events.Widget.HOVER_LEAVE -> hoveredTab = null; - case "left-click" -> { + case Events.Widget.LEFT_CLICK -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire("vtabwidget-tab-left-click", hoveredTab); + events.fire(Events.TabWidget.TAB_LEFT_CLICK, hoveredTab); setActiveTab(hoveredTab); } - case "left-click-release" -> { + case Events.Widget.LEFT_CLICK_RELEASE -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire("vtabwidget-tab-left-click-release", hoveredTab); + events.fire(Events.TabWidget.TAB_LEFT_CLICK_RELEASE, hoveredTab); } - case "middle-click" -> { + case Events.Widget.MIDDLE_CLICK -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire("vtabwidget-tab-middle-click", hoveredTab); + events.fire(Events.TabWidget.TAB_MIDDLE_CLICK, hoveredTab); } - case "middle-click-release" -> { + case Events.Widget.MIDDLE_CLICK_RELEASE -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire("vtabwidget-tab-middle-click-release", hoveredTab); + events.fire(Events.TabWidget.TAB_MIDDLE_CLICK_RELEASE, hoveredTab); } - case "right-click" -> { + case Events.Widget.RIGHT_CLICK -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire("vtabwidget-tab-right-click", hoveredTab); + events.fire(Events.TabWidget.TAB_RIGHT_CLICK, hoveredTab); } - case "right-click-release" -> { + case Events.Widget.RIGHT_CLICK_RELEASE -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire("vtabwidget-tab-right-click-release", hoveredTab); + events.fire(Events.TabWidget.TAB_RIGHT_CLICK_RELEASE, hoveredTab); } } } @@ -121,7 +122,7 @@ public int getHoveredTabIndex(int mouseX) { if (relativeX >= currentX && relativeX < currentX + totalTabWidth) { if (hoveredTab != null && hoveredTab != index) { - events.fire("vtabwidget-tab-hover-change", hoveredTab); + events.fire(Events.TabWidget.TAB_HOVER_CHANGE, hoveredTab); } hoveredTab = index; @@ -140,31 +141,31 @@ public int getHoveredTabIndex(int mouseX) { } public void onTabHoverChange(Consumer runnable) { - events.register("vtabwidget-tab-hover-change", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_HOVER_CHANGE, (args) -> runnable.accept((int) args[0])); } public void onTabLeftClick(Consumer runnable) { - events.register("vtabwidget-tab-left-click", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_LEFT_CLICK, (args) -> runnable.accept((int) args[0])); } public void onTabLeftClickRelease(Consumer runnable) { - events.register("vtabwidget-tab-left-click-release", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_LEFT_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); } public void onTabMiddleClick(Consumer runnable) { - events.register("vtabwidget-tab-middle-click", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_MIDDLE_CLICK, (args) -> runnable.accept((int) args[0])); } public void onTabMiddleClickRelease(Consumer runnable) { - events.register("vtabwidget-tab-middle-click-release", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_MIDDLE_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); } public void onTabRightClick(Consumer runnable) { - events.register("vtabwidget-tab-right-click", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_RIGHT_CLICK, (args) -> runnable.accept((int) args[0])); } public void onTabRightClickRelease(Consumer runnable) { - events.register("vtabwidget-tab-right-click-release", (args) -> runnable.accept((int) args[0])); + events.register(Events.TabWidget.TAB_RIGHT_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); } public void addTab(String tab, VWidget... widgets) { From 11d36ddcefa07b715785bd8b4afc646f8139f8ab Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:10:56 +0200 Subject: [PATCH 375/661] move to new events --- .../net/snackbag/vera/widget/VLineInput.java | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 957a7a53..100cc6c5 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -5,6 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Int; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VCharLimitedEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.modifier.VHasPlaceholderFont; @@ -97,7 +98,7 @@ public void handleBuiltinEvent(String event, Object... args) { int x = getX(); - if (event.equals("left-click")) { + if (event.equals(Events.Widget.LEFT_CLICK)) { textSelection.clear(); if (Vera.getMouseX() < x) cursorPos = 0; @@ -116,7 +117,7 @@ public String getText() { public void setText(String text) { this.text = text; - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } public boolean isSelectingText() { @@ -161,23 +162,23 @@ public String getPlaceholderText() { } public void onLineChanged(Runnable runnable) { - events.register("vline-change", runnable); + events.register(Events.LineInput.CHANGE, runnable); } public void onCursorMove(Runnable runnable) { - events.register("vline-cursor-move", runnable); + events.register(Events.LineInput.CURSOR_MOVE, runnable); } public void onCursorMoveLeft(Runnable runnable) { - events.register("vline-cursor-move-left", runnable); + events.register(Events.LineInput.CURSOR_MOVE_LEFT, runnable); } public void onCursorMoveRight(Runnable runnable) { - events.register("vline-cursor-move-right", runnable); + events.register(Events.LineInput.CURSOR_MOVE_RIGHT, runnable); } public void onAddCharLimited(VCharLimitedEvent runnable) { - events.register("vline-add-char-limited", args -> runnable.run((char) args[0])); + events.register(Events.LineInput.ADD_CHAR_LIMITED, args -> runnable.run((char) args[0])); } @Override @@ -262,32 +263,32 @@ else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && cursorPos > 0) { // Handle word navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isAltDown() && cursorPos > 0) { cursorPos = Math.max(0, jumpToWordStart(cursorPos)); - events.fire("vline-cursor-move"); - events.fire("vline-cursor-move-left"); + events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(Events.LineInput.CURSOR_MOVE_LEFT); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isAltDown() && cursorPos < text.length()) { cursorPos = Math.min(text.length(), jumpToWordEnd(cursorPos)); - events.fire("vline-cursor-move"); - events.fire("vline-cursor-move-right"); + events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(Events.LineInput.CURSOR_MOVE_LEFT); } // Handle line navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isCtrlDown()) { cursorPos = 0; - events.fire("vline-cursor-move"); - events.fire("vline-cursor-move-left"); + events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(Events.LineInput.CURSOR_MOVE_LEFT); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isCtrlDown()) { cursorPos = text.length(); - events.fire("vline-cursor-move"); - events.fire("vline-cursor-move-right"); + events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(Events.LineInput.CURSOR_MOVE_RIGHT); } // Handle character navigation else if (keyCode == GLFW.GLFW_KEY_LEFT && cursorPos > 0) { cursorPos = Math.max(0, cursorPos - 1); - events.fire("vline-cursor-move"); - events.fire("vline-cursor-move-left"); + events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(Events.LineInput.CURSOR_MOVE_LEFT); } else if (keyCode == GLFW.GLFW_KEY_RIGHT && cursorPos < text.length()) { cursorPos = Math.min(text.length(), cursorPos + 1); - events.fire("vline-cursor-move"); - events.fire("vline-cursor-move-right"); + events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(Events.LineInput.CURSOR_MOVE_RIGHT); } super.keyPressed(keyCode, scanCode, modifiers); @@ -319,12 +320,12 @@ private void handleSelectionKeyPress(int keyCode) { cursorPos = newPos; textSelection.endPos = newPos; - events.fire("vline-cursor-move"); + events.fire(Events.LineInput.CURSOR_MOVE); } private void insertText(String insertion) { if (maxChars > -1 && text.length() + insertion.length() > maxChars) { - events.fire("vline-add-char-limited", insertion.charAt(0)); + events.fire(Events.LineInput.ADD_CHAR_LIMITED, insertion.charAt(0)); return; } @@ -332,7 +333,7 @@ private void insertText(String insertion) { String back = text.substring(cursorPos); text = front + insertion + back; cursorPos += insertion.length(); - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } private void deleteSelectedText() { @@ -346,7 +347,7 @@ private void deleteSelectedText() { text = front + back; cursorPos = start; clearTextSelection(); - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } private void replaceSelectedText(String replacement) { @@ -356,7 +357,7 @@ private void replaceSelectedText(String replacement) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + replacement.length() > maxChars) { - events.fire("vline-add-char-limited", replacement.charAt(0)); + events.fire(Events.LineInput.ADD_CHAR_LIMITED, replacement.charAt(0)); return; } @@ -365,7 +366,7 @@ private void replaceSelectedText(String replacement) { text = front + replacement + back; cursorPos = start + replacement.length(); clearTextSelection(); - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } @@ -435,7 +436,7 @@ public void charTyped(char chr, int modifiers) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + 1 > maxChars) { - events.fire("vline-add-char-limited", chr); + events.fire(Events.LineInput.ADD_CHAR_LIMITED, chr); return; } @@ -445,11 +446,11 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos = start + 1; clearTextSelection(); - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } else { // Normal character insertion if (maxChars > -1 && text.length() >= maxChars) { - events.fire("vline-add-char-limited", chr); + events.fire(Events.LineInput.ADD_CHAR_LIMITED, chr); return; } @@ -458,7 +459,7 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos += 1; - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } } super.charTyped(chr, modifiers); @@ -537,7 +538,7 @@ private void deleteText(int start, int end) { builder.delete(start, end); text = builder.toString(); cursorPos = Math.min(start, text.length()); - events.fire("vline-change"); + events.fire(Events.LineInput.CHANGE); } public static class TextSelection { From 5b95658afd2e70b981bbfc7b657f0301e95789b4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:12:41 +0200 Subject: [PATCH 376/661] move to new events --- .../net/snackbag/vera/widget/VDropdown.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 77893bbd..a6e37c5e 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Int; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; @@ -86,8 +87,8 @@ app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), public void setFocused(boolean focused) { super.setFocused(focused); - if (focused) events.fire("vdropdown-selector-open"); - else events.fire("vdropdown-selector-close"); + if (focused) events.fire(Events.Dropdown.SELECTOR_OPEN); + else events.fire(Events.Dropdown.SELECTOR_CLOSE); } public VColor getItemHoverColor() { @@ -138,7 +139,7 @@ public void handleBuiltinEvent(String event, Object... args) { int y = getY(); switch (event) { - case "left-click" -> { + case Events.Widget.LEFT_CLICK -> { if (isFocused()) { Item target = getHoveredItem(); if (target != null && hoveredItem != null) { @@ -151,7 +152,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case "right-click" -> { + case Events.Widget.RIGHT_CLICK -> { if (isFocused()) { Item target = getHoveredItem(); if (target != null && hoveredItem != null) { @@ -164,7 +165,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case "middle-click" -> { + case Events.Widget.MIDDLE_CLICK -> { if (isFocused()) { Item target = getHoveredItem(); if (target != null && hoveredItem != null) { @@ -177,7 +178,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case "mouse-move" -> { + case Events.Widget.MOUSE_MOVE -> { if (!isFocused()) hoveredItem = null; else { // Get mouse position relative to the dropdown's top-left corner @@ -192,7 +193,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case "hover-leave" -> hoveredItem = null; + case Events.Widget.HOVER_LEAVE -> hoveredItem = null; } super.handleBuiltinEvent(event, args); @@ -207,15 +208,15 @@ private int getItemIndexAt(int mouseY) { } public void onItemSwitch(VItemSwitchEvent runnable) { - events.register("vdropdown-item-switch", args -> runnable.run((int) args[0])); + events.register(Events.Dropdown.ITEM_SWITCH, args -> runnable.run((int) args[0])); } public void onSelectorOpen(Runnable runnable) { - events.register("vdropdown-selector-open", runnable); + events.register(Events.Dropdown.SELECTOR_OPEN, runnable); } public void onSelectorClose(Runnable runnable) { - events.register("vdropdown-selector-close", runnable); + events.register(Events.Dropdown.SELECTOR_CLOSE, runnable); } private @Nullable Item getItemAt(int mouseX, int mouseY) { @@ -248,7 +249,7 @@ public int getSelectedItem() { public void setSelectedItem(int selectedItem) { this.selectedItem = selectedItem; - events.fire("vdropdown-item-switch", selectedItem); + events.fire(Events.Dropdown.ITEM_SWITCH, selectedItem); } public void addItem(String name) { From 3e1162ad4fdea294effa07d63a810441a4d423e9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:13:16 +0200 Subject: [PATCH 377/661] move to new events --- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index a79327c4..5d7860e3 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -6,6 +6,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VCheckedStateChange; import net.snackbag.vera.style.StyleState; @@ -45,7 +46,7 @@ public void render() { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); - if (event.equals("left-click")) setChecked(!checked); + if (event.equals(Events.Widget.LEFT_CLICK)) setChecked(!checked); } public boolean isChecked() { @@ -55,10 +56,10 @@ public boolean isChecked() { public void setChecked(boolean checked) { this.checked = checked; - events.fire("vcheckbox-checked", checked); + events.fire(Events.CheckBox.CHECKED, checked); } public void onCheckStateChange(VCheckedStateChange runnable) { - events.register("vcheckbox-checked", args -> runnable.run((boolean) args[0])); + events.register(Events.CheckBox.CHECKED, args -> runnable.run((boolean) args[0])); } } From fc801e1bbc979da7916ca079101458237b78ba90 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:13:40 +0200 Subject: [PATCH 378/661] rename event CHECKED from checked to check-state-changed --- src/main/java/net/snackbag/vera/event/Events.java | 2 +- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/event/Events.java b/src/main/java/net/snackbag/vera/event/Events.java index e20b555e..e32d96ee 100644 --- a/src/main/java/net/snackbag/vera/event/Events.java +++ b/src/main/java/net/snackbag/vera/event/Events.java @@ -40,7 +40,7 @@ public static class Widget { // Checkbox public static class CheckBox { - public static final String CHECKED = "vcheckbox-checked"; + public static final String CHECK_STATE_CHANGED = "vcheckbox-check-state-changed"; } // Dropdown diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 5d7860e3..6557c610 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -3,8 +3,6 @@ import net.minecraft.util.Identifier; import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VColor; -import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VCheckedStateChange; @@ -56,10 +54,10 @@ public boolean isChecked() { public void setChecked(boolean checked) { this.checked = checked; - events.fire(Events.CheckBox.CHECKED, checked); + events.fire(Events.CheckBox.CHECK_STATE_CHANGED, checked); } public void onCheckStateChange(VCheckedStateChange runnable) { - events.register(Events.CheckBox.CHECKED, args -> runnable.run((boolean) args[0])); + events.register(Events.CheckBox.CHECK_STATE_CHANGED, args -> runnable.run((boolean) args[0])); } } From e9a795709faca2556fe68ccfa2ac6621610b84c1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:15:27 +0200 Subject: [PATCH 379/661] move to new event system --- src/main/java/net/snackbag/vera/VElement.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 4443f616..60ac54f3 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -2,6 +2,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.EventHandler; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VWidgetMessageEvent; import net.snackbag.vera.layout.VLayout; import org.jetbrains.annotations.Nullable; @@ -69,19 +70,19 @@ public void show() { // public void onMessage(VWidgetMessageEvent executor) { - events.register("elem-message", args -> executor.run((VWidgetMessageEvent.Context) args[0])); + events.register(Events.Element.MESSAGE,args -> executor.run((VWidgetMessageEvent.Context) args[0])); } public void sendMessage(VElement element, String type, @Nullable Object content) { - element.events.fire("elem-message", new VWidgetMessageEvent.Context(this, type, content)); + element.events.fire(Events.Element.MESSAGE, new VWidgetMessageEvent.Context(this, type, content)); } public void onLayoutSwap(Consumer executor) { - events.register("elem-layout-swap", args -> executor.accept((VLayout) args[0])); + events.register(Events.Element.LAYOUT_SWAP, args -> executor.accept((VLayout) args[0])); } public void onLayoutRemove(Runnable executor) { - events.register("elem-layout-remove", args -> executor.run()); + events.register(Events.Element.LAYOUT_REMOVE, args -> executor.run()); } // From 6717c8fa2b4a71b056ca89b3c848cfc6d2b8df76 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:19:59 +0200 Subject: [PATCH 380/661] move to new event system --- src/main/java/net/snackbag/vera/util/DragHandler.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java index f1563abf..c9be1f68 100644 --- a/src/main/java/net/snackbag/vera/util/DragHandler.java +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -2,6 +2,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VMouseButton; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VMouseDragEvent; import net.snackbag.vera.widget.VWidget; import org.joml.Vector2i; @@ -75,9 +76,9 @@ else return new Vector2i( private static void fireEvents() { switch (button) { - case LEFT -> target.events.fire("mouse-drag-left", createContext()); - case MIDDLE -> target.events.fire("mouse-drag-middle", createContext()); - case RIGHT -> target.events.fire("mouse-drag-right", createContext()); + case LEFT -> target.events.fire(Events.Widget.DRAG_LEFT_CLICK, createContext()); + case MIDDLE -> target.events.fire(Events.Widget.DRAG_MIDDLE_CLICK, createContext()); + case RIGHT -> target.events.fire(Events.Widget.DRAG_RIGHT_CLICK, createContext()); } } } From 8daba6ef2c7341970d445478b7c7c87806b89ed9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:20:01 +0200 Subject: [PATCH 381/661] move to new event system --- src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index b4a9f667..a31a789c 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -7,6 +7,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.widget.VWidget; @@ -135,7 +136,7 @@ public void handleFilesDropped(List paths) { if (top != null && top.isPointOverThis(x, y)) { VWidget widget = top.getTopWidgetAt(x, y); if (widget != null) { - widget.events.fire("files-dropped", paths); + widget.events.fire(Events.Widget.FILES_DROPPED, paths); return; } } @@ -147,7 +148,7 @@ public void handleFilesDropped(List paths) { VWidget widget = app.getTopWidgetAt(x, y); if (widget != null) { - widget.events.fire("files-dropped", paths); + widget.events.fire(Events.Widget.FILES_DROPPED, paths); didSomething.set(true); } }); From 27fdbe8b4a4a96ad645366bf0315dda8810789eb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:20:03 +0200 Subject: [PATCH 382/661] move to new event system --- src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index 5c8280ff..507c34a4 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -5,6 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; @@ -35,7 +36,7 @@ public abstract class MouseMixin { if (app.isRequiresHierarchy() && app != top) return; VWidget widget = app.getTopWidgetAt(mouseX, mouseY); - if (widget != null) widget.events.fire("mouse-move", mouseX, mouseY); + if (widget != null) widget.events.fire(Events.Widget.MOUSE_MOVE, mouseX, mouseY); else if (app.getCursorShape() != VCursorShape.DEFAULT) app.setCursorShape(VCursorShape.DEFAULT); }); From 8ba5d5db945ce6386421c54697f2bd61896767b6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:20:05 +0200 Subject: [PATCH 383/661] move to new event system --- .../snackbag/mcvera/mixin/ParentElementMixin.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index fa58bc4a..1fb85bee 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -5,6 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VMouseButton; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -56,9 +57,9 @@ private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) if (widget == null) return; switch (button) { - case LEFT -> widget.events.fire("left-click"); - case RIGHT -> widget.events.fire("right-click"); - case MIDDLE -> widget.events.fire("middle-click"); + case LEFT -> widget.events.fire(Events.Widget.LEFT_CLICK); + case RIGHT -> widget.events.fire(Events.Widget.RIGHT_CLICK); + case MIDDLE -> widget.events.fire(Events.Widget.MIDDLE_CLICK); } DragHandler.down(button, widget); @@ -87,9 +88,9 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto if (widget == null) return; switch (button) { - case LEFT -> widget.events.fire("left-click-release"); - case RIGHT -> widget.events.fire("right-click-release"); - case MIDDLE -> widget.events.fire("middle-click-release"); + case LEFT -> widget.events.fire(Events.Widget.LEFT_CLICK_RELEASE); + case RIGHT -> widget.events.fire(Events.Widget.RIGHT_CLICK_RELEASE); + case MIDDLE -> widget.events.fire(Events.Widget.MIDDLE_CLICK_RELEASE); } } @@ -110,6 +111,6 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto @Unique private void handleScrollEvents(@Nullable VWidget widget, int x, int y, double amount) { if (widget == null) return; - widget.events.fire("mouse-scroll", x, y, amount); + widget.events.fire(Events.Widget.SCROLL, x, y, amount); } } From f6fd818629bd2048fb2de16207ee4838ffc1644a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:20:07 +0200 Subject: [PATCH 384/661] move to new event system --- src/main/java/net/snackbag/vera/core/VeraApp.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index a8b1f196..b4bd988c 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -3,6 +3,7 @@ import net.minecraft.client.MinecraftClient; import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; +import net.snackbag.vera.event.Events; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; @@ -222,10 +223,10 @@ public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; if (isFocusedWidget(widget)) setFocusedWidget(null); - if (widget.isLeftClickDown()) widget.events.fire("left-click-release"); - if (widget.isMiddleClickDown()) widget.events.fire("middle-click-release"); - if (widget.isRightClickDown()) widget.events.fire("right-click-release"); - if (widget.isHovered()) widget.events.fire("hover-leave"); + if (widget.isLeftClickDown()) widget.events.fire(Events.Widget.LEFT_CLICK_RELEASE); + if (widget.isMiddleClickDown()) widget.events.fire(Events.Widget.MIDDLE_CLICK_RELEASE); + if (widget.isRightClickDown()) widget.events.fire(Events.Widget.RIGHT_CLICK_RELEASE); + if (widget.isHovered()) widget.events.fire(Events.Widget.HOVER_LEAVE); this.widgets.remove(widget); } @@ -294,8 +295,8 @@ public void setFocusedWidget(@Nullable VWidget widget) { VWidget oldWidget = this.focusedWidget; this.focusedWidget = widget; - if (oldWidget != null) oldWidget.events.fire("focus-state-change"); - if (widget != null) widget.events.fire("focus-state-change"); + if (oldWidget != null) oldWidget.events.fire(Events.Widget.FOCUS_STATE_CHANGE); + if (widget != null) widget.events.fire(Events.Widget.FOCUS_STATE_CHANGE); } } From 08110442013129466680726c6a7aa61323fdaac0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 12:20:09 +0200 Subject: [PATCH 385/661] move to new event system --- src/main/java/net/snackbag/vera/layout/VLayout.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 9952cf76..ee2d8ca8 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -3,6 +3,7 @@ import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import org.joml.Vector2i; import java.util.ArrayList; @@ -61,18 +62,18 @@ public int calculateElementsHeight() { public void addElement(VElement elem) { if (elements.contains(elem)) return; elements.add(elem); - elem.events.fire("elem-layout-swap", this); + elem.events.fire(Events.Element.LAYOUT_SWAP, this); } public boolean removeElement(VElement elem) { if (!elements.contains(elem)) return false; - elem.events.fire("elem-layout-remove"); + elem.events.fire(Events.Element.LAYOUT_REMOVE); return elements.remove(elem); } public void clear() { - for (VElement elem : elements) elem.events.fire("elem-layout-remove"); + for (VElement elem : elements) elem.events.fire(Events.Element.LAYOUT_REMOVE); elements.clear(); } } From 993dc628079aa65b3b34fa31877e9352574ea6cb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:26:46 +0200 Subject: [PATCH 386/661] UnwindComposite -> WindingComposite --- src/main/java/net/snackbag/vera/core/VeraApp.java | 4 ++-- .../composite/{UnwindComposite.java => WindingComposite.java} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/net/snackbag/vera/style/animation/composite/{UnwindComposite.java => WindingComposite.java} (95%) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index b4bd988c..747e5554 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -9,7 +9,7 @@ import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.style.animation.VeraPipeline; import net.snackbag.vera.style.animation.composite.AnimationComposite; -import net.snackbag.vera.style.animation.composite.UnwindComposite; +import net.snackbag.vera.style.animation.composite.WindingComposite; import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -65,7 +65,7 @@ public VeraApp(boolean mouseRequired) { public void loadComposites() { pipeline.addPass(new AnimationComposite()); - pipeline.addPass(new UnwindComposite()); + pipeline.addPass(new WindingComposite()); } public void setCursorVisible(boolean cursorVisible) { diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java similarity index 95% rename from src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java rename to src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java index f39fd5c8..b1ae1e71 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/UnwindComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java @@ -3,7 +3,7 @@ import net.snackbag.vera.style.animation.AnimationEngine; import net.snackbag.vera.style.animation.VAnimation; -public class UnwindComposite extends Composite { +public class WindingComposite extends Composite { private long time; @Override From 5da150a9057d2c8b98715039fd7e2d3125b36b13 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:32:51 +0200 Subject: [PATCH 387/661] add kill method --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 384e9493..18d30331 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -55,6 +55,11 @@ public void activate(VAnimation animation, boolean override) { activeAnimations.put(animation, System.currentTimeMillis()); } + public void kill(VAnimation animation) { + activeAnimations.remove(animation); + unwindingAnimations.remove(animation); + } + public void unwind(VAnimation animation) { unwind(animation, false); } From 03d5c2ebb35d3ab3dae6020591fe5b83cf831f6f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:32:58 +0200 Subject: [PATCH 388/661] fire UNWIND_BEGIN event --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 18d30331..9cc8baf7 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -66,6 +66,8 @@ public void unwind(VAnimation animation) { public void unwind(VAnimation animation, boolean override) { if (!override && isUnwinding(animation.name)) return; + + widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); unwindingAnimations.put(animation, System.currentTimeMillis()); } From 16cecbdd257cc754cc5bc67c6733ea438c7b2990 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:33:03 +0200 Subject: [PATCH 389/661] fire UNWIND_BEGIN event --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 9cc8baf7..51c2e249 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -2,6 +2,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.Events; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; From 76a45b188024af21237af7a946816a6b591a6991 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:33:11 +0200 Subject: [PATCH 390/661] add REWIND_BEGIN --- src/main/java/net/snackbag/vera/event/Events.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/event/Events.java b/src/main/java/net/snackbag/vera/event/Events.java index e32d96ee..f79f09a0 100644 --- a/src/main/java/net/snackbag/vera/event/Events.java +++ b/src/main/java/net/snackbag/vera/event/Events.java @@ -6,7 +6,8 @@ public class Events { public static class Animation { public static final String BEGIN = "animation-begin"; public static final String FINISH = "animation-finish"; - public static final String UNWIND_BEGIN = "animation-unwind-begin"; // TODO: implement + public static final String UNWIND_BEGIN = "animation-unwind-begin"; + public static final String REWIND_BEGIN = "animation-rewind-begin"; } // Element From 4ead1f4732e160d9d025b2911fc7224bf2f91c4e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:33:16 +0200 Subject: [PATCH 391/661] add kill shortcut --- src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index aef5710f..69d90ab5 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -41,6 +41,7 @@ public void init() { new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)).alsoAdd(); new VShortcut(this, "b", () -> testRect.animations.unwind(testAnimation)).alsoAdd(); + new VShortcut(this, "k", () -> testRect.animations.kill(testAnimation)).alsoAdd(); } public VStyleSheet createStyleSheet() { From 905316150a1f4c37aab326f6aab57b785c92ed35 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:33:29 +0200 Subject: [PATCH 392/661] add onAnimationUnwindBegin and onAnimationRewindBegin --- .../net/snackbag/vera/event/VAnimationRewindEvent.java | 7 +++++++ .../net/snackbag/vera/event/VAnimationUnwindEvent.java | 7 +++++++ src/main/java/net/snackbag/vera/widget/VWidget.java | 8 ++++++++ 3 files changed, 22 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/event/VAnimationRewindEvent.java create mode 100644 src/main/java/net/snackbag/vera/event/VAnimationUnwindEvent.java diff --git a/src/main/java/net/snackbag/vera/event/VAnimationRewindEvent.java b/src/main/java/net/snackbag/vera/event/VAnimationRewindEvent.java new file mode 100644 index 00000000..ced849b2 --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/VAnimationRewindEvent.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.event; + +import net.snackbag.vera.style.animation.VAnimation; + +public interface VAnimationRewindEvent { + void run(VAnimation animation); +} diff --git a/src/main/java/net/snackbag/vera/event/VAnimationUnwindEvent.java b/src/main/java/net/snackbag/vera/event/VAnimationUnwindEvent.java new file mode 100644 index 00000000..d08f8b5f --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/VAnimationUnwindEvent.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.event; + +import net.snackbag.vera.style.animation.VAnimation; + +public interface VAnimationUnwindEvent { + void run(VAnimation animation); +} diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 9cc89fa8..60469438 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -249,6 +249,14 @@ public void onAnimationBegin(VAnimationBeginEvent runnable) { events.register(Events.Animation.BEGIN, args -> runnable.run((VAnimation) args[0])); } + public void onAnimationUnwindBegin(VAnimationUnwindEvent runnable) { + events.register(Events.Animation.UNWIND_BEGIN, args -> runnable.run((VAnimation) args[0])); + } + + public void onAnimationRewindBegin(VAnimationRewindEvent runnable) { + events.register(Events.Animation.REWIND_BEGIN, args -> runnable.run((VAnimation) args[0])); + } + public void onAnimationFinish(VAnimationFinishEvent runnable) { events.register(Events.Animation.FINISH, args -> runnable.run((VAnimation) args[0], (long) args[1])); } From 7f4bfea49dcc43c52042fce6dc40ea1dccb22cef Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:34:15 +0200 Subject: [PATCH 393/661] very very basic rewind that doesnt do anything but send an event --- .../snackbag/vera/style/animation/AnimationEngine.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 51c2e249..ab1daede 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -72,6 +72,16 @@ public void unwind(VAnimation animation, boolean override) { unwindingAnimations.put(animation, System.currentTimeMillis()); } + public void rewind(VAnimation animation) { + rewind(animation, false); + } + + public void rewind(VAnimation animation, boolean override) { + if (!override && isRewinding(animation.name)) return; + + widget.events.fire(Events.Animation.REWIND_BEGIN, animation); + } + public @Nullable VAnimation getIfEverActive(String name) { return activeAnimations.keySet() .stream() From db83d9d68ada1a2f8b86c38dfa5a1e450d88c503 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:47:29 +0200 Subject: [PATCH 394/661] create RewindContext map --- .../vera/style/animation/AnimationEngine.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index ab1daede..93157ddd 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -17,6 +17,7 @@ public class AnimationEngine { public final VWidget widget; private final HashMap activeAnimations = new HashMap<>(); private final HashMap unwindingAnimations = new HashMap<>(); + private final HashMap rewindingAnimations = new HashMap<>(); private long cacheId = 0; private final HashMap cache = new HashMap<>(); @@ -128,4 +129,16 @@ public long getTimeSinceActive(VAnimation animation) { public long getTimeSinceUnwinding(VAnimation animation) { return unwindingAnimations.getOrDefault(animation, -1L); } + + public @Nullable RewindContext getRewindContext(VAnimation animation) { + return rewindingAnimations.getOrDefault(animation, null); + } + + /** + * Context for animation rewinding + * + * @param since the timestamp when the animation started rewinding + * @param unwindProgress the amount of millisecond progress the unwinding has already made + */ + public record RewindContext(Long since, int unwindProgress) {} } From b9ac937d26a1d1fdb8b58343a553fce1673a185b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:47:41 +0200 Subject: [PATCH 395/661] basic rewind function --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 93157ddd..dc647931 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -78,9 +78,15 @@ public void rewind(VAnimation animation) { } public void rewind(VAnimation animation, boolean override) { + if (!unwindingAnimations.containsKey(animation)) return; if (!override && isRewinding(animation.name)) return; + long time = System.currentTimeMillis(); + widget.events.fire(Events.Animation.REWIND_BEGIN, animation); + + long unwindingSince = unwindingAnimations.remove(animation); + rewindingAnimations.put(animation, new RewindContext(time, (int) (time - unwindingSince))); } public @Nullable VAnimation getIfEverActive(String name) { From 81674e113669282d0fd916bc706f9fb69862b7f9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:47:47 +0200 Subject: [PATCH 396/661] add getAllRewinding --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index dc647931..ef9b3093 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -105,6 +105,10 @@ public VAnimation[] getAllUnwinding() { return unwindingAnimations.keySet().toArray(new VAnimation[0]); } + public VAnimation[] getAllRewinding() { + return rewindingAnimations.keySet().toArray(new VAnimation[0]); + } + public void checkCache() { if (cacheId != Vera.renderCacheId) { cache.clear(); From 4c46d9901f2bb67e62324d316bbc33fbac750870 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:48:18 +0200 Subject: [PATCH 397/661] isRewinding --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index ef9b3093..4eaf2373 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -34,6 +34,10 @@ public boolean isUnwinding(String name) { return unwindingAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } + public boolean isRewinding(String name) { + return rewindingAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); + } + /** * Unwinds animations whenever they come to their end. * Called in {@link net.snackbag.mcvera.impl.MCVeraRenderer#renderApp(VeraApp)} From cadf779e5029aa47cad4b0888f13f77673ee6bc8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:48:48 +0200 Subject: [PATCH 398/661] change unwind keybind --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 69d90ab5..3fc2b56b 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -40,7 +40,7 @@ public void init() { testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)).alsoAdd(); - new VShortcut(this, "b", () -> testRect.animations.unwind(testAnimation)).alsoAdd(); + new VShortcut(this, "u", () -> testRect.animations.unwind(testAnimation)).alsoAdd(); new VShortcut(this, "k", () -> testRect.animations.kill(testAnimation)).alsoAdd(); } From b868979bbee59c2d1c04d33279744f98ca331112 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 14:49:08 +0200 Subject: [PATCH 399/661] add rewind keybind --- src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 3fc2b56b..4e6a0f8f 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -41,6 +41,7 @@ public void init() { new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)).alsoAdd(); new VShortcut(this, "u", () -> testRect.animations.unwind(testAnimation)).alsoAdd(); + new VShortcut(this, "r", () -> testRect.animations.rewind(testAnimation)).alsoAdd(); new VShortcut(this, "k", () -> testRect.animations.kill(testAnimation)).alsoAdd(); } From 6ea4441ba823ff063a8b9844b4185d21e427a06a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 15:19:14 +0200 Subject: [PATCH 400/661] kill rewind --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 4eaf2373..0127aa95 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -64,6 +64,7 @@ public void activate(VAnimation animation, boolean override) { public void kill(VAnimation animation) { activeAnimations.remove(animation); unwindingAnimations.remove(animation); + rewindingAnimations.remove(animation); } public void unwind(VAnimation animation) { From dbc437742d4d5cfbc418b7f2fd7416f62516b581 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 15:35:30 +0200 Subject: [PATCH 401/661] basic rewinding --- .../animation/composite/WindingComposite.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java index b1ae1e71..aba5477f 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java @@ -31,6 +31,21 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); } + for (VAnimation animation : engine.getAllRewinding()) { + if (!animation.affects(ctx.style())) continue; + if (animation.unwindTime <= 0) continue; + + AnimationEngine.RewindContext rewindCtx = engine.getRewindContext(animation); + + long sinceRewinding = rewindCtx.since(); + int unwindProgress = animation.unwindTime - rewindCtx.unwindProgress(); + int relativeTime = (int) (time - sinceRewinding); + + float delta = (unwindProgress + relativeTime) / (float) animation.unwindTime; + + out = (T) ctx.type().animationTransition.apply(ctx.original(), in, animation.unwindEasing, delta); + } + return out; } } From 19dafc97c73f160b4860b4d56b8e06faf1f3e594 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 22 Jul 2025 17:12:07 +0200 Subject: [PATCH 402/661] undo --- .../animation/composite/WindingComposite.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java index aba5477f..b1ae1e71 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java @@ -31,21 +31,6 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); } - for (VAnimation animation : engine.getAllRewinding()) { - if (!animation.affects(ctx.style())) continue; - if (animation.unwindTime <= 0) continue; - - AnimationEngine.RewindContext rewindCtx = engine.getRewindContext(animation); - - long sinceRewinding = rewindCtx.since(); - int unwindProgress = animation.unwindTime - rewindCtx.unwindProgress(); - int relativeTime = (int) (time - sinceRewinding); - - float delta = (unwindProgress + relativeTime) / (float) animation.unwindTime; - - out = (T) ctx.type().animationTransition.apply(ctx.original(), in, animation.unwindEasing, delta); - } - return out; } } From bc4898b41dedd0a28ae45dc4ef068b923202db0f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 23 Jul 2025 12:38:28 +0200 Subject: [PATCH 403/661] unwinding now also has context --- .../vera/style/animation/AnimationEngine.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 0127aa95..634335f3 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -16,7 +16,7 @@ public class AnimationEngine { public final VWidget widget; private final HashMap activeAnimations = new HashMap<>(); - private final HashMap unwindingAnimations = new HashMap<>(); + private final HashMap unwindingAnimations = new HashMap<>(); private final HashMap rewindingAnimations = new HashMap<>(); private long cacheId = 0; @@ -74,8 +74,16 @@ public void unwind(VAnimation animation) { public void unwind(VAnimation animation, boolean override) { if (!override && isUnwinding(animation.name)) return; + long time = System.currentTimeMillis(); + widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); - unwindingAnimations.put(animation, System.currentTimeMillis()); + + long rewindingSince = time; + if (rewindingAnimations.containsKey(animation)) { + rewindingSince = rewindingAnimations.remove(animation).begun; + } + + unwindingAnimations.put(animation, new UnwindContext(time, (int) (time - rewindingSince))); } public void rewind(VAnimation animation) { @@ -90,7 +98,7 @@ public void rewind(VAnimation animation, boolean override) { widget.events.fire(Events.Animation.REWIND_BEGIN, animation); - long unwindingSince = unwindingAnimations.remove(animation); + long unwindingSince = unwindingAnimations.remove(animation).begun; rewindingAnimations.put(animation, new RewindContext(time, (int) (time - unwindingSince))); } @@ -141,8 +149,8 @@ public long getTimeSinceActive(VAnimation animation) { return activeAnimations.getOrDefault(animation, -1L); } - public long getTimeSinceUnwinding(VAnimation animation) { - return unwindingAnimations.getOrDefault(animation, -1L); + public @Nullable UnwindContext getTimeSinceUnwinding(VAnimation animation) { + return unwindingAnimations.getOrDefault(animation, null); } public @Nullable RewindContext getRewindContext(VAnimation animation) { @@ -152,8 +160,16 @@ public long getTimeSinceUnwinding(VAnimation animation) { /** * Context for animation rewinding * - * @param since the timestamp when the animation started rewinding - * @param unwindProgress the amount of millisecond progress the unwinding has already made + * @param begun the timestamp when the animation started rewinding + * @param unwindProgress the amount of milliseconds the animation has already been unwinding + */ + public record RewindContext(Long begun, int unwindProgress) {} + + /** + * Context for animation unwinding + * + * @param begun the timestamp when the animation started unwinding + * @param rewindProgress the amount of milliseconds the animation has already been rewinding */ - public record RewindContext(Long since, int unwindProgress) {} + public record UnwindContext(Long begun, int rewindProgress) {} } From 1c616280f63d8e7f20f92444ea1549345f94bd66 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 23 Jul 2025 16:52:47 +0200 Subject: [PATCH 404/661] fix --- .../vera/style/animation/composite/WindingComposite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java index b1ae1e71..f6c4802e 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java @@ -24,7 +24,7 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { continue; } - long sinceUnwinding = engine.getTimeSinceUnwinding(animation); + long sinceUnwinding = engine.getTimeSinceUnwinding(animation).begun(); int relativeTime = (int) (time - sinceUnwinding); float delta = Math.min((float) relativeTime / (float) animation.unwindTime, 1f); From 6d09667e740b4dcf7f94442bf54deeb1f8ede633 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 12:54:47 +0200 Subject: [PATCH 405/661] fix rewind-unwind stacking --- .../vera/style/animation/AnimationEngine.java | 20 +++++++++------- .../animation/composite/WindingComposite.java | 24 ++++++++++++++++--- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 634335f3..828fc1e3 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -75,15 +75,18 @@ public void unwind(VAnimation animation, boolean override) { if (!override && isUnwinding(animation.name)) return; long time = System.currentTimeMillis(); - widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); - long rewindingSince = time; + int rewindProgress = 0; if (rewindingAnimations.containsKey(animation)) { - rewindingSince = rewindingAnimations.remove(animation).begun; + RewindContext rewindCtx = rewindingAnimations.remove(animation); + int previousUnwindProgress = rewindCtx.unwindProgress(); + int currentRewindTime = (int) (time - rewindCtx.begun()); + + rewindProgress = Math.max(0, previousUnwindProgress - currentRewindTime); } - unwindingAnimations.put(animation, new UnwindContext(time, (int) (time - rewindingSince))); + unwindingAnimations.put(animation, new UnwindContext(time, rewindProgress)); } public void rewind(VAnimation animation) { @@ -95,11 +98,12 @@ public void rewind(VAnimation animation, boolean override) { if (!override && isRewinding(animation.name)) return; long time = System.currentTimeMillis(); - widget.events.fire(Events.Animation.REWIND_BEGIN, animation); - long unwindingSince = unwindingAnimations.remove(animation).begun; - rewindingAnimations.put(animation, new RewindContext(time, (int) (time - unwindingSince))); + UnwindContext unwindCtx = unwindingAnimations.remove(animation); + int totalUnwindProgress = unwindCtx.rewindProgress() + (int) (time - unwindCtx.begun()); + + rewindingAnimations.put(animation, new RewindContext(time, totalUnwindProgress)); } public @Nullable VAnimation getIfEverActive(String name) { @@ -149,7 +153,7 @@ public long getTimeSinceActive(VAnimation animation) { return activeAnimations.getOrDefault(animation, -1L); } - public @Nullable UnwindContext getTimeSinceUnwinding(VAnimation animation) { + public @Nullable UnwindContext getUnwindContext(VAnimation animation) { return unwindingAnimations.getOrDefault(animation, null); } diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java index f6c4802e..637c6669 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java @@ -24,9 +24,27 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { continue; } - long sinceUnwinding = engine.getTimeSinceUnwinding(animation).begun(); - int relativeTime = (int) (time - sinceUnwinding); - float delta = Math.min((float) relativeTime / (float) animation.unwindTime, 1f); + + AnimationEngine.UnwindContext unwindCtx = engine.getUnwindContext(animation); + int totalProgress = unwindCtx.rewindProgress() + (int) (time - unwindCtx.begun()); + float delta = Math.min((float) totalProgress / (float) animation.unwindTime, 1f); + + out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); + } + + for (VAnimation animation : engine.getAllRewinding()) { + if (!animation.affects(ctx.style())) continue; + if (animation.unwindTime <= 0) { + out = in; + continue; + } + + AnimationEngine.RewindContext rewindCtx = engine.getRewindContext(animation); + int currentRewindTime = (int) (time - rewindCtx.begun()); + + // calculate reverse: start from how much we had unwound, go back towards 0 + int remainingUnwindProgress = Math.max(0, rewindCtx.unwindProgress() - currentRewindTime); + float delta = Math.min((float) remainingUnwindProgress / (float) animation.unwindTime, 1f); out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); } From 98faed9f0e38818c1fab367957e84b0a7381e861 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:00:57 +0200 Subject: [PATCH 406/661] actually cache styles??? --- .../snackbag/vera/style/animation/AnimationEngine.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 828fc1e3..f738da44 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -137,10 +137,12 @@ public T animateStyle(String style, T value) { checkCache(); if (value == null) return null; - if (cache.containsKey(style)) return (T) cache.get(style); + if (!cache.containsKey(style)) { + StyleValueType type = StyleValueType.get(value, null); + cache.put(style, widget.app.pipeline.applyComposites(this, style, type, value)); + } - StyleValueType type = StyleValueType.get(value, null); - return widget.app.pipeline.applyComposites(this, style, type, value); + return (T) cache.get(style); } /** From 1390e00428207d28c482358e6f82ec755329911c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:01:04 +0200 Subject: [PATCH 407/661] microoptimizations --- .../animation/composite/WindingComposite.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java index 637c6669..ee9159ad 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java @@ -2,38 +2,54 @@ import net.snackbag.vera.style.animation.AnimationEngine; import net.snackbag.vera.style.animation.VAnimation; +import net.snackbag.vera.widget.VWidget; + +import java.util.HashMap; public class WindingComposite extends Composite { + private final HashMap unwindingAnimations = new HashMap<>(); + private final HashMap rewindingAnimations = new HashMap<>(); private long time; @Override public void generateUniforms() { time = System.currentTimeMillis(); + unwindingAnimations.clear(); + rewindingAnimations.clear(); + } + + @Override + public void applyWidget(VWidget widget) { + unwindingAnimations.put(widget.animations, widget.animations.getAllUnwinding()); + rewindingAnimations.put(widget.animations, widget.animations.getAllRewinding()); } @Override public T applyStyle(Context ctx, T in, boolean isNewFrame) { AnimationEngine engine = ctx.engine(); + VAnimation[] unwinding = unwindingAnimations.get(engine); + VAnimation[] rewinding = rewindingAnimations.get(engine); T out = in; + T original = ctx.original(); + String style = ctx.style(); - for (VAnimation animation : engine.getAllUnwinding()) { - if (!animation.affects(ctx.style())) continue; + for (VAnimation animation : unwinding) { + if (!animation.affects(style)) continue; if (animation.unwindTime <= 0) { - out = ctx.original(); + out = original; continue; } - AnimationEngine.UnwindContext unwindCtx = engine.getUnwindContext(animation); int totalProgress = unwindCtx.rewindProgress() + (int) (time - unwindCtx.begun()); float delta = Math.min((float) totalProgress / (float) animation.unwindTime, 1f); - out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); + out = (T) ctx.type().animationTransition.apply(in, original, animation.unwindEasing, delta); } - for (VAnimation animation : engine.getAllRewinding()) { - if (!animation.affects(ctx.style())) continue; + for (VAnimation animation : rewinding) { + if (!animation.affects(style)) continue; if (animation.unwindTime <= 0) { out = in; continue; @@ -46,7 +62,7 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { int remainingUnwindProgress = Math.max(0, rewindCtx.unwindProgress() - currentRewindTime); float delta = Math.min((float) remainingUnwindProgress / (float) animation.unwindTime, 1f); - out = (T) ctx.type().animationTransition.apply(in, ctx.original(), animation.unwindEasing, delta); + out = (T) ctx.type().animationTransition.apply(in, original, animation.unwindEasing, delta); } return out; From 4b090c7cd6a2e814b0526f18ee8aba2a51286cf6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:13:24 +0200 Subject: [PATCH 408/661] test hover animation --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 4e6a0f8f..c4d150e2 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -21,6 +21,11 @@ public class StyleTestApplication extends VeraApp { .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) .build(); + private final VAnimation hoverAnimation = new VAnimation.Builder(this, "hover") + .unwindTime(100) + .keyframe(100, frame -> frame.style("background-color", VColor.MC_WHITE), 0) + .build(); + @Override public void init() { new VShortcut(this, "escape", this::hide).alsoAdd(); @@ -32,7 +37,9 @@ public void init() { .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); - testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); + testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); + testRect.onHoverLeave(() -> testRect.animations.unwind(hoverAnimation)); + //testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); From de67c21becc524920ad15cc48c59f2636fef4b8f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:13:43 +0200 Subject: [PATCH 409/661] add activateOrRewind method --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index f738da44..356a0265 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -61,6 +61,11 @@ public void activate(VAnimation animation, boolean override) { activeAnimations.put(animation, System.currentTimeMillis()); } + public void activateOrRewind(VAnimation animation) { + if (isUnwinding(animation.name)) rewind(animation); + else activate(animation); + } + public void kill(VAnimation animation) { activeAnimations.remove(animation); unwindingAnimations.remove(animation); From 8b01f3a276d484ab898ef849aa2557cf46b5aa70 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:13:52 +0200 Subject: [PATCH 410/661] dont unwind if the animation isnt running --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 356a0265..802a46d3 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -78,6 +78,7 @@ public void unwind(VAnimation animation) { public void unwind(VAnimation animation, boolean override) { if (!override && isUnwinding(animation.name)) return; + if (!(activeAnimations.containsKey(animation) || !rewindingAnimations.containsKey(animation))) return; long time = System.currentTimeMillis(); widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); From 8dd60e93579f105c0c68405e2b17eb5cd734f454 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:15:18 +0200 Subject: [PATCH 411/661] add isActive method and rename old isActive to isUnbothered --- .../snackbag/vera/style/animation/AnimationEngine.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 802a46d3..334a5b40 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -27,6 +27,10 @@ public AnimationEngine(VWidget widget) { } public boolean isActive(String name) { + return isUnbothered(name) || isUnwinding(name) || isRewinding(name); + } + + public boolean isUnbothered(String name) { return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } @@ -57,7 +61,7 @@ public void activate(VAnimation animation) { } public void activate(VAnimation animation, boolean override) { - if (!override && isActive(animation.name)) return; + if (!override && isUnbothered(animation.name)) return; activeAnimations.put(animation, System.currentTimeMillis()); } @@ -78,7 +82,7 @@ public void unwind(VAnimation animation) { public void unwind(VAnimation animation, boolean override) { if (!override && isUnwinding(animation.name)) return; - if (!(activeAnimations.containsKey(animation) || !rewindingAnimations.containsKey(animation))) return; + if (!(isUnbothered(animation.name) || !isRewinding(animation.name))) return; long time = System.currentTimeMillis(); widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); From 9568ce9574ed33ef1b9c8a51c54eab3e868b8f4c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:19:02 +0200 Subject: [PATCH 412/661] undo isUnbothered shenanigans --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 334a5b40..2d62472c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -27,10 +27,6 @@ public AnimationEngine(VWidget widget) { } public boolean isActive(String name) { - return isUnbothered(name) || isUnwinding(name) || isRewinding(name); - } - - public boolean isUnbothered(String name) { return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); } @@ -61,7 +57,7 @@ public void activate(VAnimation animation) { } public void activate(VAnimation animation, boolean override) { - if (!override && isUnbothered(animation.name)) return; + if (!override && isActive(animation.name)) return; activeAnimations.put(animation, System.currentTimeMillis()); } From 7df512d2e04f3e6895dd1b09fa5203d3e4446de2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:19:38 +0200 Subject: [PATCH 413/661] check if is active before unwinding --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 2d62472c..20615acf 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -77,6 +77,7 @@ public void unwind(VAnimation animation) { } public void unwind(VAnimation animation, boolean override) { + if (!isActive(animation.name)) return; if (!override && isUnwinding(animation.name)) return; if (!(isUnbothered(animation.name) || !isRewinding(animation.name))) return; From 8cabcd4ec3bb53b92366511a3caacb115f10f24f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:19:44 +0200 Subject: [PATCH 414/661] check if is active before unwinding --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 20615acf..d43a06f5 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -79,7 +79,6 @@ public void unwind(VAnimation animation) { public void unwind(VAnimation animation, boolean override) { if (!isActive(animation.name)) return; if (!override && isUnwinding(animation.name)) return; - if (!(isUnbothered(animation.name) || !isRewinding(animation.name))) return; long time = System.currentTimeMillis(); widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); From 75984c885f01893b39e7f68d4657487e9658f7b0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:19:50 +0200 Subject: [PATCH 415/661] check if is active before rewinding --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index d43a06f5..20d2a270 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -100,6 +100,7 @@ public void rewind(VAnimation animation) { } public void rewind(VAnimation animation, boolean override) { + if (!isActive(animation.name)) return; if (!unwindingAnimations.containsKey(animation)) return; if (!override && isRewinding(animation.name)) return; From e2ab7f99e328434caa13f30cbe550c7763c837a1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:27:10 +0200 Subject: [PATCH 416/661] auto kill --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 20d2a270..14486df9 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -49,6 +49,8 @@ public void update() { if (time - getTimeSinceActive(animation) >= animation.getTotalTime() - animation.unwindTime) { unwind(animation); } + + if (time - getTimeSinceActive(animation) >= animation.getTotalTime()) kill(animation); } } From 21344f79e85643a9fda6a66d0d4d28210a5201ae Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:27:20 +0200 Subject: [PATCH 417/661] use unwindOnFinish --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index c4d150e2..041eb79a 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -16,6 +16,7 @@ public class StyleTestApplication extends VeraApp { private final VAnimation testAnimation = new VAnimation.Builder(this, "test") .unwindTime(2000) + .unwindOnFinish() .keyframe(1000, frame -> frame.style("background-color", VColor.MC_GOLD), 2000) .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) @@ -23,6 +24,7 @@ public class StyleTestApplication extends VeraApp { private final VAnimation hoverAnimation = new VAnimation.Builder(this, "hover") .unwindTime(100) + .keyframe(100, frame -> frame.style("background-color", VColor.MC_WHITE), 0) .build(); From 1613d5d9349a517bb9b81c6fca13717799bdf5b0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:27:32 +0200 Subject: [PATCH 418/661] give autoUnwindAtEnd for monotone end --- .../vera/style/animation/composite/AnimationComposite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java index 867b8fda..b5d6a38b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java @@ -50,7 +50,7 @@ public T applyStyle(Context ctx, T in, boolean isNewFrame) { ctx.original(), ctx.type(), animationTimes.get(animation), - true + animation.autoUnwindAtEnd ); if (rv != null) out = rv; From feef5d95262d97ef8f0a6dd7c7965c42c9afb0bb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 24 Jul 2025 13:27:49 +0200 Subject: [PATCH 419/661] add autoUnwindAtEnd setting for animations --- .../snackbag/vera/style/animation/VAnimation.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 80f2ab34..921437da 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -25,6 +25,7 @@ public class VAnimation { public final VeraApp app; public final int unwindTime; + public final boolean autoUnwindAtEnd; public final VEasing unwindEasing; public final LoopMode loopMode; @@ -33,9 +34,10 @@ public class VAnimation { private int totalTime = 0; - public VAnimation(String name, int unwindTime, VEasing unwindEasing, LoopMode loopMode, VeraApp app) { + public VAnimation(String name, int unwindTime, boolean autoUnwindAtEnd, VEasing unwindEasing, LoopMode loopMode, VeraApp app) { this.name = name; this.unwindTime = unwindTime; + this.autoUnwindAtEnd = autoUnwindAtEnd; this.unwindEasing = unwindEasing; this.totalTime = unwindTime; this.loopMode = loopMode; @@ -125,6 +127,7 @@ public static class Builder { private LoopMode loopMode = LoopMode.NONE; private int unwindTime = 0; + private boolean autoUnwindAtEnd = false; private VEasing unwindEasing = Easings.LINEAR; private final List>> keyframes = new ArrayList<>(); @@ -144,6 +147,11 @@ public Builder unwindTime(int ms) { return this; } + public Builder unwindOnFinish() { + this.autoUnwindAtEnd = true; + return this; + } + public Builder unwindEasing(VEasing easing) { this.unwindEasing = easing; return this; @@ -155,7 +163,7 @@ public Builder keyframe(int transitionMs, Consumer frame, int stayMs) } public VAnimation build() { - VAnimation animation = new VAnimation(name, unwindTime, unwindEasing, loopMode, app); + VAnimation animation = new VAnimation(name, unwindTime, autoUnwindAtEnd, unwindEasing, loopMode, app); for (Pair> frame : keyframes) { animation.addKeyframe(frame.getA()); From c1ac46d59318f54c3ec649498a7df358d966cf5a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 27 Jul 2025 14:44:50 +0200 Subject: [PATCH 420/661] cut back on loop modes --- src/main/java/net/snackbag/vera/style/animation/LoopMode.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/LoopMode.java b/src/main/java/net/snackbag/vera/style/animation/LoopMode.java index 4beded31..0e998196 100644 --- a/src/main/java/net/snackbag/vera/style/animation/LoopMode.java +++ b/src/main/java/net/snackbag/vera/style/animation/LoopMode.java @@ -2,6 +2,5 @@ public enum LoopMode { NONE, - REPEAT, - REPEAT_REVERSE + REPEAT } From 4a2dc0828888f3594c2a47b186be4068b15d6040 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 18 Aug 2025 17:14:49 +0200 Subject: [PATCH 421/661] call animation begin event --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 14486df9..9aaa1c8d 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -61,6 +61,7 @@ public void activate(VAnimation animation) { public void activate(VAnimation animation, boolean override) { if (!override && isActive(animation.name)) return; activeAnimations.put(animation, System.currentTimeMillis()); + widget.events.fire(Events.Animation.BEGIN, animation); } public void activateOrRewind(VAnimation animation) { From 3db42929be2bd62dc998c7dad3ad434d7202f02e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 18 Aug 2025 18:07:38 +0200 Subject: [PATCH 422/661] test looping --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 041eb79a..51f15e03 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -7,6 +7,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.style.animation.LoopMode; import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -17,6 +18,7 @@ public class StyleTestApplication extends VeraApp { private final VAnimation testAnimation = new VAnimation.Builder(this, "test") .unwindTime(2000) .unwindOnFinish() + .loop(LoopMode.REPEAT) .keyframe(1000, frame -> frame.style("background-color", VColor.MC_GOLD), 2000) .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) From 6d0248156ed08da12d1417500018c92dc2135c65 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 18 Aug 2025 18:08:11 +0200 Subject: [PATCH 423/661] fix ConcurrentModificationException --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 9aaa1c8d..39322b6b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -45,7 +45,8 @@ public boolean isRewinding(String name) { public void update() { final long time = System.currentTimeMillis(); - for (VAnimation animation : activeAnimations.keySet()) { + HashMap animations = (HashMap) activeAnimations.clone(); + for (VAnimation animation : animations.keySet()) { if (time - getTimeSinceActive(animation) >= animation.getTotalTime() - animation.unwindTime) { unwind(animation); } From cc2e307f05f902bb1f551ecf9fdbe06c1e661e09 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 18 Aug 2025 18:08:23 +0200 Subject: [PATCH 424/661] kill ifn't looping --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 39322b6b..71f47c4e 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.Objects; /** * Per-widget handler for animations. This is after stylesheet.getKey, so there is no differentiation between @@ -51,7 +52,8 @@ public void update() { unwind(animation); } - if (time - getTimeSinceActive(animation) >= animation.getTotalTime()) kill(animation); + if (time - getTimeSinceActive(animation) >= animation.getTotalTime() && animation.loopMode == LoopMode.NONE) + kill(animation); } } From 98c86039ea838ff3bfcb26e68b2a3f37631e2cd4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 18 Aug 2025 18:08:32 +0200 Subject: [PATCH 425/661] call animation finish event --- .../vera/style/animation/AnimationEngine.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 71f47c4e..4d2cc299 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -73,9 +73,16 @@ public void activateOrRewind(VAnimation animation) { } public void kill(VAnimation animation) { - activeAnimations.remove(animation); - unwindingAnimations.remove(animation); - rewindingAnimations.remove(animation); + Long begin = activeAnimations.remove(animation); + UnwindContext unwindCtx = unwindingAnimations.remove(animation); + RewindContext rewindCtx = rewindingAnimations.remove(animation); + + Long unwindBegun = unwindCtx != null ? unwindCtx.begun : null; + Long rewindBegun = rewindCtx != null ? rewindCtx.begun : null; + + Long nullableBegun = Vera.firstOf(Objects::nonNull, begin, unwindBegun, rewindBegun); + + if (nullableBegun != null) widget.events.fire(Events.Animation.FINISH, animation, nullableBegun); } public void unwind(VAnimation animation) { From 67b2146f10b6f4e042bc1e0ed0c90feab5a17f00 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:24:08 +0200 Subject: [PATCH 426/661] move widgets to new system --- .../java/net/snackbag/vera/widget/VWidget.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 60469438..0ea60f57 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -268,22 +268,22 @@ public void handleBuiltinEvent(String event, Object... args) { prevStyleState = state; switch (event) { - case "left-click" -> { + case Events.Widget.LEFT_CLICK -> { if (focusOnClick) { setFocused(true); } leftClickDown = true; } - case "right-click" -> rightClickDown = true; - case "middle-click" -> middleClickDown = true; + case Events.Widget.RIGHT_CLICK -> rightClickDown = true; + case Events.Widget.MIDDLE_CLICK -> middleClickDown = true; - case "left-click-release" -> clearLeftClickDown(); - case "right-click-release" -> clearRightClickDown(); - case "middle-click-release" -> clearMiddleClickDown(); + case Events.Widget.LEFT_CLICK_RELEASE -> clearLeftClickDown(); + case Events.Widget.RIGHT_CLICK_RELEASE -> clearRightClickDown(); + case Events.Widget.MIDDLE_CLICK_RELEASE -> clearMiddleClickDown(); - case "hover" -> app.setCursorShape(getStyle("cursor", state)); - case "hover-leave" -> { + case Events.Widget.HOVER -> app.setCursorShape(getStyle("cursor", state)); + case Events.Widget.HOVER_LEAVE -> { clearLeftClickDown(); clearRightClickDown(); clearMiddleClickDown(); From 5c371f1a093b681a2f106f628bf53b8690f40310 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:48:04 +0200 Subject: [PATCH 427/661] fix hover state being handled incorrectly --- .../java/net/snackbag/vera/widget/VWidget.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 0ea60f57..89a0e946 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -282,17 +282,27 @@ public void handleBuiltinEvent(String event, Object... args) { case Events.Widget.RIGHT_CLICK_RELEASE -> clearRightClickDown(); case Events.Widget.MIDDLE_CLICK_RELEASE -> clearMiddleClickDown(); - case Events.Widget.HOVER -> app.setCursorShape(getStyle("cursor", state)); case Events.Widget.HOVER_LEAVE -> { clearLeftClickDown(); clearRightClickDown(); clearMiddleClickDown(); } } + } - state = createStyleState(); - if (state != prevStyleState) update(); - prevStyleState = state; + @Override + public void afterBuiltinEvent(String name, Object... args) { + updateIfNeeded(); + } + + private void updateIfNeeded() { + StyleState state = createStyleState(); + if (state != prevStyleState) { + update(); + prevStyleState = state; + + System.out.println("updated"); + } } private void clearLeftClickDown() { From 50d05d370d8750169342b6ca5632ee88004565b1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:48:10 +0200 Subject: [PATCH 428/661] fix hover state being handled incorrectly --- src/main/java/net/snackbag/vera/widget/VWidget.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 89a0e946..d8897a9f 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -263,10 +263,6 @@ public void onAnimationFinish(VAnimationFinishEvent runnable) { @Override public void handleBuiltinEvent(String event, Object... args) { - StyleState state = createStyleState(); - if (state != prevStyleState) update(); - prevStyleState = state; - switch (event) { case Events.Widget.LEFT_CLICK -> { if (focusOnClick) { From d832920c200939615ec9e7c4c6d3c074749ff532 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:48:21 +0200 Subject: [PATCH 429/661] add QOL firstOf method --- src/main/java/net/snackbag/vera/Vera.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index 781747ee..58136e15 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; public class Vera { public static final MCVeraProvider provider = new MCVeraProvider(); @@ -91,4 +92,14 @@ public static int getMouseY() { public static boolean isTopHierarchy(VeraApp app) { return getTopHierarchyApp() == app; } + + @SafeVarargs + public static @Nullable T firstOf(Predicate evaluator, T... values) { + for (T v : values) { + if (v == null) continue; + if (evaluator.test(v)) return v; + } + + return null; + } } From 9596eb074bbb15311486e0030682c80034ec8891 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:48:48 +0200 Subject: [PATCH 430/661] add event postprocessor and therefore rename PreProcessorContext -> Processor --- .../net/snackbag/vera/event/EventHandler.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/event/EventHandler.java b/src/main/java/net/snackbag/vera/event/EventHandler.java index 6fc9a338..0a01d5d5 100644 --- a/src/main/java/net/snackbag/vera/event/EventHandler.java +++ b/src/main/java/net/snackbag/vera/event/EventHandler.java @@ -9,7 +9,9 @@ public class EventHandler { public final VElement element; - public @Nullable PreProcessorContext preprocessor; + + public @Nullable EventHandler.Processor preprocessor; // called before event stack execution + public @Nullable EventHandler.Processor postprocessor; // called after event stack execution private final HashMap> executors = new HashMap<>(); @@ -20,8 +22,17 @@ public EventHandler(VElement element) { public void fire(String name, Object... args) { if (preprocessor != null) preprocessor.call(name, args); - if (!executors.containsKey(name)) return; + if (!executors.containsKey(name)) { + doPostProcessor(name, args); + return; + } + executors.get(name).parallelStream().forEach(e -> e.run(args)); + doPostProcessor(name, args); + } + + private void doPostProcessor(String name, Object... args) { + if (postprocessor != null) postprocessor.call(name, args); } public void register(String name, VEvent executor) { @@ -41,7 +52,7 @@ public void clear(String name) { } @FunctionalInterface - public interface PreProcessorContext { + public interface Processor { void call(String name, Object[] args); } } From 320ac21b0919e8e065c4a15115b1d016ef076164 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:48:59 +0200 Subject: [PATCH 431/661] increase keyframe time for testing --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 51f15e03..956aaa98 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -27,7 +27,7 @@ public class StyleTestApplication extends VeraApp { private final VAnimation hoverAnimation = new VAnimation.Builder(this, "hover") .unwindTime(100) - .keyframe(100, frame -> frame.style("background-color", VColor.MC_WHITE), 0) + .keyframe(1000, frame -> frame.style("background-color", VColor.MC_WHITE), 0) .build(); @Override From 09d4fec777dd74f9800181149ad66a35e07028f3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:49:14 +0200 Subject: [PATCH 432/661] attempt to make a hover style with transition --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 956aaa98..6cd13f83 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -42,8 +42,13 @@ public void init() { VRect testRect = new VRect(VColor.black(), this).alsoAdd(); testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); - testRect.onHoverLeave(() -> testRect.animations.unwind(hoverAnimation)); - //testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); + testRect.onHoverLeave(() -> { + testRect.setStyle("background-color", StyleState.HOVERED, VColor.black()); + testRect.animations.unwind(hoverAnimation); + }); + testRect.onAnimationFinish((animation, time) -> { + testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); + }); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); From be3cba9856980dbb703b6c3d5a11e1d5a06e1316 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:49:31 +0200 Subject: [PATCH 433/661] implement event postprocessor as afterBuiltinEvent --- src/main/java/net/snackbag/vera/VElement.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 60ac54f3..5cb25815 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -31,6 +31,7 @@ public VElement(VeraApp app, int x, int y, int width, int height) { this.events = new EventHandler(this); this.events.preprocessor = this::handleBuiltinEvent; + this.events.postprocessor = this::afterBuiltinEvent; addVisibilityCondition(() -> visible); @@ -44,6 +45,7 @@ public VElement(VeraApp app, int x, int y, int width, int height) { } public void handleBuiltinEvent(String name, Object... args) {} + public void afterBuiltinEvent(String name, Object... args) {} // // Visibility From 8cc3eb52d6da079762babd1da506ac62d0b98818 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:49:53 +0200 Subject: [PATCH 434/661] fire finish event --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 4d2cc299..caf3a9cd 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -53,7 +53,9 @@ public void update() { } if (time - getTimeSinceActive(animation) >= animation.getTotalTime() && animation.loopMode == LoopMode.NONE) + widget.events.fire(Events.Animation.FINISH, animation, getTimeSinceActive(animation)); kill(animation); + } } } From 55cb958ecd9bc366c04c205d1ef3c4b34f3fa134 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 19 Aug 2025 14:50:01 +0200 Subject: [PATCH 435/661] fire finish event --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index caf3a9cd..882c9709 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -52,7 +52,7 @@ public void update() { unwind(animation); } - if (time - getTimeSinceActive(animation) >= animation.getTotalTime() && animation.loopMode == LoopMode.NONE) + if (time - getTimeSinceActive(animation) >= animation.getTotalTime() && animation.loopMode == LoopMode.NONE) { widget.events.fire(Events.Animation.FINISH, animation, getTimeSinceActive(animation)); kill(animation); } From 595c52c368f4f20580f11b4e5adb3ee139c6eafb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 16:29:10 +0200 Subject: [PATCH 436/661] add all() to ColorModifier --- src/main/java/net/snackbag/vera/core/VColor.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 5cb59255..122ed2ab 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -200,6 +200,16 @@ public ColorModifier rgba(int r, int g, int b, float a) { return this; } + public ColorModifier all(int all) { + rgb(all, all, all); + return this; + } + + public ColorModifier rgb(VColor color) { + rgba(color.red, color.green, color.blue, color.opacity); + return this; + } + public ColorModifier red(int r) { color = color.withRed(r); colorUpdater.accept(color); From b354a4cfb318f9b8404b66c1351b9e7df1f0a54f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 16:29:18 +0200 Subject: [PATCH 437/661] remove debug "updated" println --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index d8897a9f..aafdf2cf 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -296,8 +296,6 @@ private void updateIfNeeded() { if (state != prevStyleState) { update(); prevStyleState = state; - - System.out.println("updated"); } } From cd99f9581f8ff3aa646174cd1131afdc29dd80a9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:35:08 +0200 Subject: [PATCH 438/661] very basic alignment --- src/main/java/net/snackbag/vera/layout/VLayout.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index ee2d8ca8..ea211148 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.Events; +import net.snackbag.vera.flag.VLayoutAlignmentFlag; import org.joml.Vector2i; import java.util.ArrayList; @@ -12,6 +13,7 @@ public abstract class VLayout extends VElement { protected final List elements = new ArrayList<>(); + public VLayoutAlignmentFlag alignment = VLayoutAlignmentFlag.START; /** * ID for the last position cache call. To be compared with the current render cache ID From 93291035b032e23f273c01df9777fd6e00d75ca7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:35:16 +0200 Subject: [PATCH 439/661] height is now height and not max height --- src/main/java/net/snackbag/vera/layout/VLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index ea211148..ca1e4cac 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -34,7 +34,7 @@ public VLayout(VeraApp app, int x, int y, int width, int height) { @Override public int getHeight() { - return height < 0 ? calculateElementsHeight() : Math.min(calculateElementsHeight(), height); + return height < 0 ? calculateElementsHeight() : height; } public Vector2i posOf(VElement elem) { From e295ec10ccd095440856f7a2eb2fe13e79a70efb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:35:36 +0200 Subject: [PATCH 440/661] do applyAlignment when posOf (should probably get cached though) --- src/main/java/net/snackbag/vera/layout/VLayout.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index ca1e4cac..70c6853b 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -41,7 +41,7 @@ public Vector2i posOf(VElement elem) { checkCache(); if (!cache.containsKey(elem)) throw new RuntimeException("Layout cache does not contain requested element '%s'".formatted(elem.toString())); - return cache.get(elem); + return applyAlignment(cache.get(elem)); } private void checkCache() { @@ -53,6 +53,7 @@ private void checkCache() { } } + protected abstract Vector2i applyAlignment(Vector2i original); public abstract void rebuild(); public int calculateElementsHeight() { From 37f4a9542bac967b549a94ff1bef367d5b589014 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:35:42 +0200 Subject: [PATCH 441/661] add VLayoutAlignmentFlag --- .../java/net/snackbag/vera/flag/VLayoutAlignmentFlag.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/flag/VLayoutAlignmentFlag.java diff --git a/src/main/java/net/snackbag/vera/flag/VLayoutAlignmentFlag.java b/src/main/java/net/snackbag/vera/flag/VLayoutAlignmentFlag.java new file mode 100644 index 00000000..9114ba3b --- /dev/null +++ b/src/main/java/net/snackbag/vera/flag/VLayoutAlignmentFlag.java @@ -0,0 +1,7 @@ +package net.snackbag.vera.flag; + +public enum VLayoutAlignmentFlag { + START, + CENTER, + END +} From 900546f9433cdaa5d85f59cb41076a787575e0a4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:35:51 +0200 Subject: [PATCH 442/661] calculate alignment for vertical --- src/main/java/net/snackbag/vera/layout/VVLayout.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 4a75d5c1..bc468866 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -32,4 +32,13 @@ public void rebuild() { y += elem.getHeight(); } } + + @Override + protected Vector2i applyAlignment(Vector2i original) { + return switch (alignment) { + case START -> original; + case CENTER -> new Vector2i(original.x, getHeight() / 2 - calculateElementsHeight() / 2 + original.y / 2); + case END -> new Vector2i(original.x, getHeight() - calculateElementsHeight() + original.y); + }; + } } From 44ac4141b682ffdfcddba422dac24202383f15b2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:44:13 +0200 Subject: [PATCH 443/661] calculate horizontal alignment --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index 183c81f0..e7b71605 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -32,4 +32,13 @@ public void rebuild() { x += elem.getWidth(); } } + + @Override + protected Vector2i applyAlignment(Vector2i original) { + return switch (alignment) { + case START -> original; + case CENTER -> new Vector2i(getWidth() / 2 - calculateElementsWidth() / 2 + original.x, original.y); + case END -> new Vector2i(getWidth() - calculateElementsWidth() + original.x, original.y); + }; + } } From 8025b8b8e3835725b0a9e4ef13c6fdfbf192be3e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:44:26 +0200 Subject: [PATCH 444/661] fix getting width --- .../java/net/snackbag/vera/layout/VLayout.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 70c6853b..fd28b402 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -27,10 +27,10 @@ public VLayout(VeraApp app, int x, int y, int width, int height) { super(app, x, y, width, height); } -// @Override -// public int getWidth() { -// return width < 0 ? calculateElementsWidth() : Math.min(calculateElementsWidth(), width); -// } + @Override + public int getWidth() { + return width < 0 ? calculateElementsWidth() : width; + } @Override public int getHeight() { @@ -62,6 +62,12 @@ public int calculateElementsHeight() { .sum(); } + public int calculateElementsWidth() { + return elements.stream() + .mapToInt(VElement::getWidth) + .max().orElse(1); + } + public void addElement(VElement elem) { if (elements.contains(elem)) return; elements.add(elem); From 175a7c70d1316f6addd9474ab0f739d6aa1ddc69 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 19:44:36 +0200 Subject: [PATCH 445/661] parallelStream -> stream for less overhead --- src/main/java/net/snackbag/vera/layout/VLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index fd28b402..1f0b18cf 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -57,7 +57,7 @@ private void checkCache() { public abstract void rebuild(); public int calculateElementsHeight() { - return elements.parallelStream() + return elements.stream() .mapToInt(VElement::getHeight) .sum(); } From 1377f992a9a26713a9f6b39cfde40a5206533d95 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 20:40:32 +0200 Subject: [PATCH 446/661] add the ability to keep the final style --- .../vera/style/animation/AnimationEngine.java | 8 +++++ .../vera/style/animation/VAnimation.java | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 882c9709..64c23c92 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -54,6 +54,14 @@ public void update() { if (time - getTimeSinceActive(animation) >= animation.getTotalTime() && animation.loopMode == LoopMode.NONE) { widget.events.fire(Events.Animation.FINISH, animation, getTimeSinceActive(animation)); + + if (animation.writeFinalStateTarget != null) { + HashMap finalStyles = animation.getFinalStyles(); + for (String key : finalStyles.keySet()) { + widget.setStyle(key, animation.writeFinalStateTarget, finalStyles.get(key)); + } + } + kill(animation); } } diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 921437da..8a89da28 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -1,6 +1,7 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.style.animation.easing.VEasing; @@ -28,19 +29,26 @@ public class VAnimation { public final boolean autoUnwindAtEnd; public final VEasing unwindEasing; public final LoopMode loopMode; + public final @Nullable StyleState writeFinalStateTarget; private final List keyframes = new ArrayList<>(); protected final HashMap styleAffections = new HashMap<>(); private int totalTime = 0; - public VAnimation(String name, int unwindTime, boolean autoUnwindAtEnd, VEasing unwindEasing, LoopMode loopMode, VeraApp app) { + public VAnimation( + String name, + int unwindTime, boolean autoUnwindAtEnd, VEasing unwindEasing, + LoopMode loopMode, @Nullable StyleState writeFinalStateTarget, + VeraApp app + ) { this.name = name; this.unwindTime = unwindTime; this.autoUnwindAtEnd = autoUnwindAtEnd; this.unwindEasing = unwindEasing; this.totalTime = unwindTime; this.loopMode = loopMode; + this.writeFinalStateTarget = writeFinalStateTarget; this.app = app; } @@ -102,6 +110,17 @@ public T getStyleForKeyframeDeep(int targetIndex, String style) { return (T) StyleValueType.convert(pair.getB(), pair.getA()); } + public HashMap getFinalStyles() { + HashMap finalStyles = new HashMap<>(); + + if (keyframes.isEmpty()) return finalStyles; + for (String style : styleAffections.keySet()) { + finalStyles.put(style, getStyleForKeyframeDeep(keyframes.size() - 1, style)); + } + + return finalStyles; + } + public int getTotalTime() { return totalTime; } @@ -126,6 +145,7 @@ public static class Builder { private final VeraApp app; private LoopMode loopMode = LoopMode.NONE; + private @Nullable StyleState keepFinalStyle = null; private int unwindTime = 0; private boolean autoUnwindAtEnd = false; private VEasing unwindEasing = Easings.LINEAR; @@ -157,13 +177,18 @@ public Builder unwindEasing(VEasing easing) { return this; } + public Builder keepFinalStyle(StyleState writeTo) { + this.keepFinalStyle = writeTo; + return this; + } + public Builder keyframe(int transitionMs, Consumer frame, int stayMs) { keyframes.add(new Pair<>(new VKeyframe(transitionMs, stayMs), frame)); return this; } public VAnimation build() { - VAnimation animation = new VAnimation(name, unwindTime, autoUnwindAtEnd, unwindEasing, loopMode, app); + VAnimation animation = new VAnimation(name, unwindTime, autoUnwindAtEnd, unwindEasing, loopMode, keepFinalStyle, app); for (Pair> frame : keyframes) { animation.addKeyframe(frame.getA()); From 3d206da0ab124704b2450351891c2b5eb806032b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 20:40:49 +0200 Subject: [PATCH 447/661] add keepFinalStyle on hoverAnimation --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 6cd13f83..83719c8a 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -25,7 +25,8 @@ public class StyleTestApplication extends VeraApp { .build(); private final VAnimation hoverAnimation = new VAnimation.Builder(this, "hover") - .unwindTime(100) + .unwindTime(1000) + .keepFinalStyle(StyleState.HOVERED) .keyframe(1000, frame -> frame.style("background-color", VColor.MC_WHITE), 0) .build(); From 87467c7d9256fe30c4ced778b1f9677c7b5a6517 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 20:40:55 +0200 Subject: [PATCH 448/661] new testing --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 83719c8a..f647dea7 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -43,13 +43,7 @@ public void init() { VRect testRect = new VRect(VColor.black(), this).alsoAdd(); testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); - testRect.onHoverLeave(() -> { - testRect.setStyle("background-color", StyleState.HOVERED, VColor.black()); - testRect.animations.unwind(hoverAnimation); - }); - testRect.onAnimationFinish((animation, time) -> { - testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); - }); + //testRect.onHoverLeave(() -> testRect.animations.unwindOrActivateReversed(hoverAnimation)); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); From cde45ebfc7fb5923ff7a93c7f60872d85be48ba1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 21:18:52 +0200 Subject: [PATCH 449/661] add effective transform --- src/main/java/net/snackbag/vera/VElement.java | 16 +++++++++++++ .../net/snackbag/vera/widget/VDropdown.java | 14 +++++------ .../java/net/snackbag/vera/widget/VLabel.java | 16 ++++++------- .../net/snackbag/vera/widget/VLineInput.java | 16 ++++++------- .../net/snackbag/vera/widget/VTabWidget.java | 8 +++---- .../net/snackbag/vera/widget/VWidget.java | 24 +++++++++---------- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 5cb25815..8ac7b39e 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -99,6 +99,14 @@ public int getY() { return layout != null ? layout.posOf(this).y : _y; } + public int getEffectiveX() { + return getX(); + } + + public int getEffectiveY() { + return getY(); + } + public void move(int both) { move(both, both); } @@ -116,6 +124,14 @@ public int getHeight() { return height; } + public int getEffectiveWidth() { + return getWidth(); + } + + public int getEffectiveHeight() { + return getHeight(); + } + public void setWidth(int width) { setSize(width, height); } diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index a6e37c5e..1b87666d 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -44,7 +44,7 @@ public void render() { int y = getY(); Vera.renderer.drawRect( - app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), + app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), 0, backgroundColor ); @@ -59,9 +59,9 @@ app, getHitboxX(), getHitboxY(), getHitboxWidth(), getHitboxHeight(), if (isHovered) { Vera.renderer.drawRect( app, - getHitboxX(), + getEffectiveX(), y + (i * (itemSpacing + font.getSize() / 2)), - getHitboxWidth(), + getEffectiveWidth(), font.getSize() / 2 + itemSpacing, 0, itemHoverColor ); @@ -104,25 +104,25 @@ public VColor.ColorModifier modifyItemHoverColor() { } @Override - public int getHitboxX() { + public int getEffectiveX() { V4Int padding = getStyle("padding", createStyleState()); return getX() - padding.get3(); } @Override - public int getHitboxY() { + public int getEffectiveY() { V4Int padding = getStyle("padding", createStyleState()); return getY() - padding.get1(); } @Override - public int getHitboxWidth() { + public int getEffectiveWidth() { V4Int padding = getStyle("padding", createStyleState()); return width + padding.get3() + padding.get4(); } @Override - public int getHitboxHeight() { + public int getEffectiveHeight() { StyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index c2c7a6fd..6890fb8b 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -34,25 +34,25 @@ public void setText(String text) { } @Override - public int getHitboxWidth() { + public int getEffectiveWidth() { V4Int padding = getStyle("padding", createStyleState()); return width + padding.get3() + padding.get4(); } @Override - public int getHitboxHeight() { + public int getEffectiveHeight() { V4Int padding = getStyle("padding", createStyleState()); return height + padding.get1() + padding.get2(); } @Override - public int getHitboxX() { + public int getEffectiveX() { V4Int padding = getStyle("padding", createStyleState()); return getX() - padding.get4(); } @Override - public int getHitboxY() { + public int getEffectiveY() { V4Int padding = getStyle("padding", createStyleState()); return getY() - padding.get1(); } @@ -94,10 +94,10 @@ public void render() { Vera.renderer.drawRect( app, - getHitboxX(), - getHitboxY(), - getHitboxWidth(), - getHitboxHeight(), + getEffectiveX(), + getEffectiveY(), + getEffectiveWidth(), + getEffectiveHeight(), rotation, backgroundColor ); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 100cc6c5..76cebb81 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -46,10 +46,10 @@ public void render() { Vera.renderer.drawRect( app, - getHitboxX() + app.getX(), - getHitboxY() + app.getY(), - getHitboxWidth(), - getHitboxHeight(), + getEffectiveX() + app.getX(), + getEffectiveY() + app.getY(), + getEffectiveWidth(), + getEffectiveHeight(), rotation, backgroundColor ); @@ -395,7 +395,7 @@ public VColor getCursorColorSafe() { } @Override - public int getHitboxWidth() { + public int getEffectiveWidth() { StyleState state = createStyleState(); VFont font = getStyle("font", state); @@ -405,7 +405,7 @@ public int getHitboxWidth() { } @Override - public int getHitboxHeight() { + public int getEffectiveHeight() { StyleState state = createStyleState(); VFont font = getStyle("font", state); @@ -416,13 +416,13 @@ public int getHitboxHeight() { @Override - public int getHitboxX() { + public int getEffectiveX() { V4Int padding = getStyle("padding", createStyleState()); return getX() - padding.get4(); } @Override - public int getHitboxY() { + public int getEffectiveY() { V4Int padding = getStyle("padding", createStyleState()); return getY() - padding.get1(); } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index fdae2c66..62a78f89 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -46,7 +46,7 @@ public void render() { Vera.renderer.drawRect(app, x + marginX - itemSpacingLeft, y, itemSpacingLeft + itemSpacingRight + textWidth, - getHitboxHeight(), 0, + getEffectiveHeight(), 0, activeTab != null && activeTab == i ? selectedBackgroundColor: defaultBackgroundColor ); @@ -112,7 +112,7 @@ public int getHoveredTabIndex(int mouseX) { int itemSpacingLeft = getStyle("item-spacing-left", state); int itemSpacingRight = getStyle("item-spacing-right", state); - int relativeX = mouseX - getHitboxX(); + int relativeX = mouseX - getEffectiveX(); int currentX = 0; int index = 0; @@ -198,12 +198,12 @@ public void addWidget(String tab, List> widgets) { } @Override - public int getHitboxHeight() { + public int getEffectiveHeight() { return ((VFont) getStyle("font", createStyleState())).getSize() / 2 + 4; } @Override - public int getHitboxWidth() { + public int getEffectiveWidth() { StyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index aafdf2cf..d15f6a58 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -38,19 +38,19 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { public abstract void render(); public int getHitboxX() { - return getX(); + return getEffectiveX(); } public int getHitboxY() { - return getY(); + return getEffectiveY(); } public int getHitboxWidth() { - return getWidth(); + return getEffectiveWidth(); } public int getHitboxHeight() { - return getHeight(); + return getEffectiveHeight(); } @SuppressWarnings("unchecked") @@ -109,27 +109,27 @@ public void renderBorder() { V4Int borderSize = getStyle("border-size", state); // Top - Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() - borderSize.get1(), getHitboxWidth(), borderSize.get1(), 0, borderColor.get1()); + Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY() - borderSize.get1(), getEffectiveWidth(), borderSize.get1(), 0, borderColor.get1()); if (borderSize.get3() > 0) { - Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY() - borderSize.get1(), borderSize.get3(), borderSize.get1(), 0, borderColor.get1()); + Vera.renderer.drawRect(app, getEffectiveX() - borderSize.get3(), getEffectiveY() - borderSize.get1(), borderSize.get3(), borderSize.get1(), 0, borderColor.get1()); } // Bottom - Vera.renderer.drawRect(app, getHitboxX(), getHitboxY() + getHitboxHeight(), getHitboxWidth(), borderSize.get2(), 0, borderColor.get2()); + Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY() + getEffectiveHeight(), getEffectiveWidth(), borderSize.get2(), 0, borderColor.get2()); if (borderSize.get4() > 0) { - Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY() + getHitboxHeight(), borderSize.get4(), borderSize.get2(), 0, borderColor.get2()); + Vera.renderer.drawRect(app, getEffectiveX() + getEffectiveWidth(), getEffectiveY() + getEffectiveHeight(), borderSize.get4(), borderSize.get2(), 0, borderColor.get2()); } // Left - Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY(), borderSize.get3(), getHitboxHeight(), 0, borderColor.get3()); + Vera.renderer.drawRect(app, getEffectiveX() - borderSize.get3(), getEffectiveY(), borderSize.get3(), getEffectiveHeight(), 0, borderColor.get3()); if (borderSize.get2() > 0) { - Vera.renderer.drawRect(app, getHitboxX() - borderSize.get3(), getHitboxY() + getHitboxHeight(), borderSize.get3(), borderSize.get2(), 0, borderColor.get3()); + Vera.renderer.drawRect(app, getEffectiveX() - borderSize.get3(), getEffectiveY() + getEffectiveHeight(), borderSize.get3(), borderSize.get2(), 0, borderColor.get3()); } // Right - Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY(), borderSize.get4(), getHitboxHeight(), 0, borderColor.get4()); + Vera.renderer.drawRect(app, getEffectiveX() + getEffectiveWidth(), getEffectiveY(), borderSize.get4(), getEffectiveHeight(), 0, borderColor.get4()); if (borderSize.get1() > 0) { - Vera.renderer.drawRect(app, getHitboxX() + getHitboxWidth(), getHitboxY() - borderSize.get1(), borderSize.get4(), borderSize.get1(), 0, borderColor.get4()); + Vera.renderer.drawRect(app, getEffectiveX() + getEffectiveWidth(), getEffectiveY() - borderSize.get1(), borderSize.get4(), borderSize.get1(), 0, borderColor.get4()); } } From 6b2be3c976afb74703ae6cba86bdc841cb4df6a8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 21:19:00 +0200 Subject: [PATCH 450/661] label padding is 0 by default now --- .../net/snackbag/vera/style/standard/LabelStandardStyle.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java index 8a441a8d..03d87ca0 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java @@ -12,7 +12,7 @@ public class LabelStandardStyle implements VStandardStyle { public void apply(VStyleSheet sheet) { sheet.setKey(VLabel.class, "background-color", VColor.transparent()); sheet.setKey(VLabel.class, "font", VFont.create()); - sheet.setKey(VLabel.class, "padding", new V4Int(4)); + sheet.setKey(VLabel.class, "padding", new V4Int(0)); } @Override From 90f841522bfb790355d405f68c6f8d91fdf3313d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 21:37:18 +0200 Subject: [PATCH 451/661] use effective height & width --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 2 +- src/main/java/net/snackbag/vera/layout/VLayout.java | 4 ++-- src/main/java/net/snackbag/vera/layout/VVLayout.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index e7b71605..7379df62 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -29,7 +29,7 @@ public void rebuild() { for (VElement elem : elements) { cache.put(elem, new Vector2i(x, getY())); - x += elem.getWidth(); + x += elem.getEffectiveWidth(); } } diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 1f0b18cf..c7943d8d 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -58,13 +58,13 @@ private void checkCache() { public int calculateElementsHeight() { return elements.stream() - .mapToInt(VElement::getHeight) + .mapToInt(VElement::getEffectiveHeight) .sum(); } public int calculateElementsWidth() { return elements.stream() - .mapToInt(VElement::getWidth) + .mapToInt(VElement::getEffectiveWidth) .max().orElse(1); } diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index bc468866..5c889a50 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -29,7 +29,7 @@ public void rebuild() { for (VElement elem : elements) { cache.put(elem, new Vector2i(getX(), y)); - y += elem.getHeight(); + y += elem.getEffectiveHeight(); } } From 751e7523584590a864c7d77c4335f8daa874ae15 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 21:39:40 +0200 Subject: [PATCH 452/661] render overlay --- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index d15f6a58..2bc8d70f 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -136,7 +136,7 @@ public void renderBorder() { public void renderOverlay() { StyleState state = createStyleState(); - Vera.renderer.drawRect(app, getX(), getY(), width, height, 0, getStyle("overlay", state)); + Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), 0, getStyle("overlay", state)); } public boolean isLeftClickDown() { From 3233d1a8f446a009d3967c7ad4e270efe482eb56 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 21:51:16 +0200 Subject: [PATCH 453/661] rework label rendering --- .../java/net/snackbag/vera/widget/VLabel.java | 32 +++++-------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 6890fb8b..937a5e80 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -45,18 +45,6 @@ public int getEffectiveHeight() { return height + padding.get1() + padding.get2(); } - @Override - public int getEffectiveX() { - V4Int padding = getStyle("padding", createStyleState()); - return getX() - padding.get4(); - } - - @Override - public int getEffectiveY() { - V4Int padding = getStyle("padding", createStyleState()); - return getY() - padding.get1(); - } - public VHAlignmentFlag getAlignment() { return alignment; } @@ -89,27 +77,23 @@ public void render() { VColor backgroundColor = getStyle("background-color", state); V4Int padding = getStyle("padding", state); - int x = getX(); - int y = getY(); - Vera.renderer.drawRect( app, - getEffectiveX(), - getEffectiveY(), + getX(), + getY(), getEffectiveWidth(), getEffectiveHeight(), rotation, backgroundColor ); + int usualX = getX() + padding.get3(); + int usualY = getY() + padding.get1(); + switch (alignment) { - case LEFT -> Vera.renderer.drawText(app, x, y, rotation, text, font); - case CENTER -> { - int textWidth = Vera.provider.getTextWidth(text, font); - int centerX = getHitboxX() + (getHitboxWidth() - textWidth) / 2; - Vera.renderer.drawText(app, centerX, y, rotation, text, font); - } - case RIGHT -> Vera.renderer.drawText(app, getHitboxX() + getHitboxWidth() - padding.get4() - Vera.provider.getTextWidth(text, font), y, rotation, text, font); + case LEFT -> Vera.renderer.drawText(app, usualX, usualY, rotation, text, font); + case CENTER -> Vera.renderer.drawText(app, getX() + getWidth() / 2 - Vera.provider.getTextWidth(text, font) / 2, usualY, rotation, text, font); + case RIGHT -> Vera.renderer.drawText(app, getX() + getEffectiveWidth() - padding.get4() - Vera.provider.getTextWidth(text, font), usualY, rotation, text, font); } } } From 7c74f399bdcb527be74c76a19c7451378ef4dfed Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 20 Aug 2025 21:51:42 +0200 Subject: [PATCH 454/661] note new discoveries --- src/main/java/net/snackbag/vera/widget/VDropdown.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 1b87666d..e0940611 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -16,6 +16,7 @@ // TODO: Rewrite VDropdown from scratch // 16/7/2025 jesus christ what a shitty thing. dont even bother making this work nice. // rewrite scheduled for once we have VCompound +// 20/8/2025 oh my public class VDropdown extends VWidget implements VHasFont { private final List items; public VFont itemHoverFont; From a6bff5418d46e352f8422192b9cc3d5afe95c09e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 25 Aug 2025 22:39:06 +0200 Subject: [PATCH 455/661] fix center alignment halving original position --- src/main/java/net/snackbag/vera/layout/VVLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 5c889a50..9dba13bc 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -37,7 +37,7 @@ public void rebuild() { protected Vector2i applyAlignment(Vector2i original) { return switch (alignment) { case START -> original; - case CENTER -> new Vector2i(original.x, getHeight() / 2 - calculateElementsHeight() / 2 + original.y / 2); + case CENTER -> new Vector2i(original.x, getHeight() / 2 - calculateElementsHeight() / 2 + original.y); case END -> new Vector2i(original.x, getHeight() - calculateElementsHeight() + original.y); }; } From efc1be6f423d69bd8dafb890f16524358c5dcdb5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 26 Aug 2025 12:41:00 +0200 Subject: [PATCH 456/661] Deprecate alsoAdd() and automatically add to add on object initialization --- src/main/java/net/snackbag/vera/event/VShortcut.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/event/VShortcut.java b/src/main/java/net/snackbag/vera/event/VShortcut.java index 1430a587..81848dd5 100644 --- a/src/main/java/net/snackbag/vera/event/VShortcut.java +++ b/src/main/java/net/snackbag/vera/event/VShortcut.java @@ -3,6 +3,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; import org.apache.commons.lang3.SystemUtils; +import org.jetbrains.annotations.ApiStatus; public class VShortcut { public final VeraApp app; @@ -19,6 +20,8 @@ public VShortcut(VeraApp app, String combination, Runnable event, boolean transf this.combination = combination.toLowerCase().replace(" ", ""); this.event = event; this.transformOSX = transformOSX; + + this.app.addShortcut(this); } public String getCombination() { @@ -45,6 +48,12 @@ public void run() { Vera.provider.handleRunShortcut(this); } + /** + * Deprecated since version 1.10, will be removed in 1.11. No longer needed since the app now already receives the + * shortcut on shortcut initialization. + */ + @Deprecated(forRemoval = true, since = "1.10") + @ApiStatus.ScheduledForRemoval(inVersion = "1.11") public VShortcut alsoAdd() { app.addShortcut(this); return this; From a4aababb2ff61e14c95913fb7a7c9c6bebfa6ed5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 26 Aug 2025 12:41:46 +0200 Subject: [PATCH 457/661] remove .alsoAdd from shortcuts --- .../snackbag/mcvera/test/LayoutTestApplication.java | 2 +- .../net/snackbag/mcvera/test/StyleTestApplication.java | 10 +++++----- .../java/net/snackbag/mcvera/test/TestApplication.java | 7 ++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java index a1fafd99..f978d4b8 100644 --- a/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/LayoutTestApplication.java @@ -12,7 +12,7 @@ public class LayoutTestApplication extends VeraApp { @Override public void init() { - new VShortcut(this, "escape", this::hide).alsoAdd(); + new VShortcut(this, "escape", this::hide); VLayout layout = new VVLayout(this, 0, 0); new VLabel("I'm a test", this).alsoAddTo(layout); diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index f647dea7..c644da96 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -33,7 +33,7 @@ public class StyleTestApplication extends VeraApp { @Override public void init() { - new VShortcut(this, "escape", this::hide).alsoAdd(); + new VShortcut(this, "escape", this::hide); mergeStyleSheet(createStyleSheet()); @@ -50,10 +50,10 @@ public void init() { testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); - new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)).alsoAdd(); - new VShortcut(this, "u", () -> testRect.animations.unwind(testAnimation)).alsoAdd(); - new VShortcut(this, "r", () -> testRect.animations.rewind(testAnimation)).alsoAdd(); - new VShortcut(this, "k", () -> testRect.animations.kill(testAnimation)).alsoAdd(); + new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)); + new VShortcut(this, "u", () -> testRect.animations.unwind(testAnimation)); + new VShortcut(this, "r", () -> testRect.animations.rewind(testAnimation)); + new VShortcut(this, "k", () -> testRect.animations.kill(testAnimation)); } public VStyleSheet createStyleSheet() { diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index fcfb3d08..80c84255 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -22,7 +22,7 @@ public TestApplication() { @Override public void init() { - VShortcut exit = new VShortcut(this, "escape", () -> { + new VShortcut(this, "escape", () -> { if (hasFocusedWidget()) { setFocusedWidget(null); return; @@ -31,13 +31,10 @@ public void init() { this.hide(); }); - VShortcut changeMouseRequired = new VShortcut(this, "leftalt+m", () -> { + new VShortcut(this, "leftalt+m", () -> { setMouseRequired(!isMouseRequired()); }); - addShortcut(exit); - addShortcut(changeMouseRequired); - VLineInput input = new VLineInput(this).alsoAdd(); input.setMaxChars(15); input.setPlaceholderText("Enter text..."); From e67e782d1b138cff9618c7288f5d7e841a2b4f43 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 26 Aug 2025 12:41:59 +0200 Subject: [PATCH 458/661] remove debug enable shortcut --- src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index a31a789c..1555088a 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -21,11 +21,6 @@ public void handleAppInitialization(VeraApp app) { Vera.registrar.applyStandardWidgetStyles(app.styleSheet); MinecraftClient.getInstance().send(app::init); MinecraftClient.getInstance().send(app::update); - - app.addShortcut(new VShortcut(app, "LeftCtrl+LeftAlt+LeftShift+D", () -> { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.of("Debug mode enabled")); - MCVeraData.debugApps.add(app); - }, false)); } public void handleAppShow(VeraApp app) { From 7aa3870aed397cf1d86e23c9b8ba466e92e3139e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 26 Aug 2025 12:42:09 +0200 Subject: [PATCH 459/661] fully remove debug mode --- .../java/net/snackbag/mcvera/MCVeraData.java | 1 - .../snackbag/mcvera/mixin/VeraAppMixin.java | 22 ------------------- 2 files changed, 23 deletions(-) delete mode 100644 src/main/java/net/snackbag/mcvera/mixin/VeraAppMixin.java diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index 5d0adef7..f75b54c2 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -14,7 +14,6 @@ public class MCVeraData { public static List appHierarchy = new ArrayList<>(); public static int appsWithMouseRequired = 0; - public static final Set debugApps = new HashSet<>(); public static final List pressedKeys = new ArrayList<>(); public static List previousPressedKeys = new ArrayList<>(); diff --git a/src/main/java/net/snackbag/mcvera/mixin/VeraAppMixin.java b/src/main/java/net/snackbag/mcvera/mixin/VeraAppMixin.java deleted file mode 100644 index c0202c6a..00000000 --- a/src/main/java/net/snackbag/mcvera/mixin/VeraAppMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.snackbag.mcvera.mixin; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; -import net.snackbag.mcvera.MCVeraData; -import net.snackbag.mcvera.impl.MCVeraProvider; -import net.snackbag.vera.core.VeraApp; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(VeraApp.class) -public abstract class VeraAppMixin { - @Inject(at = @At("HEAD"), method = "handleShortcut", remap = false) - private void mcvera$handleShortcut(String combination, CallbackInfo ci) { - VeraApp instance = (VeraApp) (Object) this; - if (!MCVeraData.debugApps.contains(instance)) return; - - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.of("§e§l[DEBUG]§f: (shortcut) " + combination)); - } -} From 1217ded2595b3151d7181425c5d9f748a69adadf Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 26 Aug 2025 14:09:54 +0200 Subject: [PATCH 460/661] empty layout alignment test app --- .../test/LayoutCenteringTestApplication.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/test/LayoutCenteringTestApplication.java diff --git a/src/main/java/net/snackbag/mcvera/test/LayoutCenteringTestApplication.java b/src/main/java/net/snackbag/mcvera/test/LayoutCenteringTestApplication.java new file mode 100644 index 00000000..de6c7aa8 --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/test/LayoutCenteringTestApplication.java @@ -0,0 +1,15 @@ +package net.snackbag.mcvera.test; + +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.VShortcut; + +public class LayoutCenteringTestApplication extends VeraApp { + public static LayoutCenteringTestApplication INSTANCE = new LayoutCenteringTestApplication(); + + @Override + public void init() { + new VShortcut(this, "escape", this::hide); + + + } +} From a03810dc66e8257e08a366f58bbc8703c8c59e64 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 25 Sep 2025 20:45:00 +0200 Subject: [PATCH 461/661] fix mixin --- src/main/resources/mcvera.mixins.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/mcvera.mixins.json b/src/main/resources/mcvera.mixins.json index 46321a8d..f762fbef 100644 --- a/src/main/resources/mcvera.mixins.json +++ b/src/main/resources/mcvera.mixins.json @@ -3,8 +3,7 @@ "package": "net.snackbag.mcvera.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ - "MinecraftClientMixin", - "VeraAppMixin" + "MinecraftClientMixin" ], "injectors": { "defaultRequire": 1 From d47aa41cd48817f4d57ff951bb8438396aa44668 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 25 Sep 2025 22:12:28 +0200 Subject: [PATCH 462/661] add featureset --- README.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e356a472..83b4010a 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,37 @@ Because UI should be simple. Vera is a simple yet powerful Fabric UI library. There are currently no plans for a Forge version.\ -[Submit a Logo](https://github.com/snackbag/vera/issues/new) [Visit Wiki](https://wiki.snackbag.net/w/vera) \ No newline at end of file +[Submit a Logo](https://github.com/snackbag/vera/issues/new) [Visit Wiki](https://wiki.snackbag.net/w/vera) + +## Features + +- Various standard widgets + - Labels + - Checkboxes + - Dropdowns + - Images + - Text input + - Tabs + - Rectangles + - Easy creation of custom widgets +- Styling system written from the ground up +- Customizable animation system + - Style-composite rendering pipeline + - (Custom) easings! +- Layout system +- HUD-rendering +- 2D rendering developer QOL +- App hierarchy +- Simple keybindings +- Heavy optimization +- (Developer) QOL with [Verto](https://github.com/snackbag/verto) +- Extensive documentation + +### Coming soon + +- Full docstrings everywhere +- More standard composites +- Multi-versions +- More precise font options +- Vertex & fragment shaders +- Revised rendering API \ No newline at end of file From 6dd49d4c991dd20537d2e6291c91880f76761aa7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 11 Oct 2025 23:21:32 +0200 Subject: [PATCH 463/661] fix animation unwinding random keyframe halt --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 64c23c92..994ee02c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -48,7 +48,7 @@ public void update() { HashMap animations = (HashMap) activeAnimations.clone(); for (VAnimation animation : animations.keySet()) { - if (time - getTimeSinceActive(animation) >= animation.getTotalTime() - animation.unwindTime) { + if ((time - getTimeSinceActive(animation) >= animation.getTotalTime() - animation.unwindTime) && animation.autoUnwindAtEnd) { unwind(animation); } diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 8a89da28..f24ebbc8 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -46,7 +46,7 @@ public VAnimation( this.unwindTime = unwindTime; this.autoUnwindAtEnd = autoUnwindAtEnd; this.unwindEasing = unwindEasing; - this.totalTime = unwindTime; + this.totalTime = autoUnwindAtEnd ? unwindTime : 0; this.loopMode = loopMode; this.writeFinalStateTarget = writeFinalStateTarget; From 492d4d7c920cf058634887a67d5b8bf29e5398ba Mon Sep 17 00:00:00 2001 From: JXSnack Date: Thu, 23 Oct 2025 14:01:25 +0200 Subject: [PATCH 464/661] add INTERNAL_TRANSITION_ANIMATION_NAME --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index f24ebbc8..5529bbb2 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -8,10 +8,7 @@ import org.jetbrains.annotations.Nullable; import oshi.util.tuples.Pair; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Consumer; /** @@ -22,6 +19,8 @@ * generates a new instance of the animation for each target app. */ public class VAnimation { + public static final String INTERNAL_TRANSITION_ANIMATION_NAME = "vera-transition"; + public final String name; public final VeraApp app; From bba50ce2c8c95dfdd1ce4624ba2bf5d5f73401b5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 24 Oct 2025 20:18:06 +0200 Subject: [PATCH 465/661] add more to roadmap --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 83b4010a..19f8f618 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,7 @@ Vera is a simple yet powerful Fabric UI library. There are currently no plans fo - Multi-versions - More precise font options - Vertex & fragment shaders -- Revised rendering API \ No newline at end of file +- Revised rendering API +- Double buffer rendering +- Vanilla-UI abilities +- Widget Compounds \ No newline at end of file From 320739d71f1a81496ea1da0a49fa5d0f43dc3f6f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:02:29 +0200 Subject: [PATCH 466/661] animation toString --- .../vera/style/animation/VAnimation.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 5529bbb2..b2e250e7 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -135,6 +135,41 @@ public int hashCode() { return Objects.hash(name, app); } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append(" (" + totalTime + "ms) {"); + builder.append("\n"); + + for (VKeyframe frame : keyframes) { + builder.append(" -> "); + builder.append(frame.transitionTime); + builder.append("ms -- "); + builder.append(frame.stayTime); + builder.append("ms: "); + builder.append(frame.easeIn); + builder.append("\n"); + + + for (String style : frame.styles.keySet()) { + Pair val = frame.styles.get(style); + + builder.append(" "); + builder.append(style); + builder.append(": "); + builder.append(val.getB()); + builder.append(" ("); + builder.append(val.getA()); + builder.append(")\n"); + } + } + + builder.append("}"); + + return builder.toString(); + } + public VKeyframe[] getKeyframes() { return keyframes.toArray(new VKeyframe[0]); } From d747dbc25efd8ff72a72a7f34625ba64321e395e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:09:29 +0200 Subject: [PATCH 467/661] rename methods same -> isSame and sameColors -> hasSameColors for clarity and mark old as deprecated --- .../java/net/snackbag/vera/core/VColor.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 122ed2ab..ab5a807f 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -1,6 +1,7 @@ package net.snackbag.vera.core; import net.snackbag.vera.style.animation.easing.VEasing; +import org.jetbrains.annotations.ApiStatus; import java.util.function.Consumer; @@ -91,19 +92,43 @@ public boolean isTransparent() { return opacity == 0; } + @Deprecated(since = "1.10", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.11") public boolean sameColors(int red, int green, int blue) { - return this.red == red && this.green == green && this.blue == blue; + return hasSameColors(red, green, blue); } + @Deprecated(since = "1.10", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.11") public boolean sameColors(VColor color) { + return hasSameColors(color); + } + + public boolean hasSameColors(int red, int green, int blue) { + return this.red == red && this.green == green && this.blue == blue; + } + + public boolean hasSameColors(VColor color) { return sameColors(color.red, color.green, color.blue); } + @Deprecated(since = "1.10", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.11") public boolean same(int red, int green, int blue, float opacity) { - return sameColors(red, green, blue) && this.opacity == opacity; + return isSame(red, green, blue, opacity); } + @Deprecated(since = "1.10", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "1.11") public boolean same(VColor color) { + return isSame(color); + } + + public boolean isSame(int red, int green, int blue, float opacity) { + return hasSameColors(red, green, blue) && this.opacity == opacity; + } + + public boolean isSame(VColor color) { return same(color.red, color.green, color.blue, color.opacity); } From f0fba4be3bc6c36f66ae25aec83b5bc846642ab5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:15:42 +0200 Subject: [PATCH 468/661] get keys methods --- .../net/snackbag/vera/style/VStyleSheet.java | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 3303ff2e..99147da5 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -7,8 +7,7 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Array; -import java.util.HashMap; -import java.util.LinkedHashSet; +import java.util.*; public class VStyleSheet { private final StyleContainer> widgetSpecificStyles = new StyleContainer<>(); // like HTML #IDs @@ -45,6 +44,45 @@ public T getKey(VWidget widget, String key, @Nullable StyleState state) { return getStandardKey(widget.getClass(), key, state); } + /** + * In this case, stacked means that also all keys from states below the + * given state are returned. + */ + public Set getKeysStacked(VWidget widget, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + + Set keys = new HashSet<>(); + + // standard styles + keys.addAll(standardStyles.getKeysStacked(widget.getClass(), state)); + + // class styles + for (String clazz : widget.classes) { + keys.addAll(classStyles.getKeysStacked(clazz, state)); + } + + // widget specific + keys.addAll(widgetSpecificStyles.getKeysStacked(widget, state)); + + return keys; + } + + /** + * Note: the resolved keys will return keys from states below the given state + */ + public HashMap getResolvedKeys(VWidget widget, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + + Set keys = getKeysStacked(widget, state); + HashMap buffer = new HashMap<>(); + + for (String key : keys) { + buffer.put(key, getKey(widget, key, state)); + } + + return buffer; + } + /** * Resolves a standard style key by traversing the class hierarchy and style states. *

From 45567a5b2d831b45022ddeccedbf86d8638a1b83 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:15:44 +0200 Subject: [PATCH 469/661] get keys methods --- .../snackbag/vera/style/StyleContainer.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleContainer.java b/src/main/java/net/snackbag/vera/style/StyleContainer.java index 377f17f7..8a3ef522 100644 --- a/src/main/java/net/snackbag/vera/style/StyleContainer.java +++ b/src/main/java/net/snackbag/vera/style/StyleContainer.java @@ -1,6 +1,10 @@ package net.snackbag.vera.style; +import org.jetbrains.annotations.Nullable; + import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; /** * Holds types, keys and states. @@ -39,6 +43,99 @@ public V getState(T part, String key, StyleState state) { return (V) getKey(part, key).get(state); } + + /** + * In this case, exact means that it does not resolve lower states and only + * gives the keys of exactly the given style state. Use {@link #getKeysStacked(Object, StyleState)} + * for deeper state resolve. + *

+ * For example when requesting state HOVERED: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
KeyStateReturned
src + * DEFAULT + * No + *
overlay + * HOVERED + * Yes + *
fontCLICKEDNo
+ * + * @see #getKeysStacked(Object, StyleState) + */ + public Set getKeysExact(T part, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + + Set buffer = new HashSet<>(); + + var resolvedPart = getPart(part); // i'm sorry for using var but holy fuck + for (String key : resolvedPart.keySet()) { + for (StyleState keyState : resolvedPart.get(key).keySet()) { + if (keyState != state) continue; + buffer.add(key); + } + } + + return buffer; + } + + /** + * In this case, stacked means that also all keys from states below the + * given state are returned. Use {@link #getKeysExact(Object, StyleState)} for + * only the exact keys of a style state. + *

+ * For example when requesting state HOVERED: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
KeyStateReturned
src + * DEFAULT + * Yes + *
overlay + * HOVERED + * Yes + *
fontCLICKEDNo
+ * + * @see #getKeysExact(Object, StyleState) + */ + public Set getKeysStacked(T part, @Nullable StyleState state) { + if (state == null) state = StyleState.DEFAULT; + + Set buffer = new HashSet<>(); + + StyleState next = state; + while (next != null) { + buffer.addAll(getKeysExact(part, next)); + next = next.fallback; + } + + return buffer; + } + public void put(T part, String key, StyleState state, Object value) { if (!hasPart(part)) values.put(part, new HashMap<>()); if (!hasKey(part, key)) values.get(part).put(key, new HashMap<>()); From 8abfc4c1d33ba0d95b3f5b56e7691ecefdc89348 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:15:58 +0200 Subject: [PATCH 470/661] get keys method --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 994ee02c..bd2ac163 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -67,6 +67,10 @@ public void update() { } } + public HashMap getAffectedStyles(StyleState state) { + return widget.app.styleSheet.getResolvedKeys(widget, state); + } + public void activate(VAnimation animation) { activate(animation, false); } From 3aefa46cd235e67933a450516e6fd4ef6d2bb365 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:16:19 +0200 Subject: [PATCH 471/661] transition animation --- .../vera/style/animation/AnimationEngine.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index bd2ac163..f7e8a5ff 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -3,7 +3,9 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.Events; +import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -86,6 +88,38 @@ public void activateOrRewind(VAnimation animation) { else activate(animation); } + public void activateTransition(StyleState from, StyleState target, int time, VEasing easing) { + activate(createTransitionAnimation(from, target, time, easing)); + } + + public VAnimation createTransitionAnimation(StyleState from, StyleState target, int time, VEasing easing) { + VAnimation.Builder builder = new VAnimation.Builder(widget.app, VAnimation.INTERNAL_TRANSITION_ANIMATION_NAME); + + builder.unwindEasing(easing); + builder.unwindTime(time); + + HashMap fromStyles = getAffectedStyles(from); + HashMap targetStyles = getAffectedStyles(target); + fromStyles.forEach(targetStyles::putIfAbsent); + targetStyles.forEach(fromStyles::putIfAbsent); + + // beginning keyframe + builder.keyframe(0, frame -> { + for (String key : fromStyles.keySet()) { + frame.style(key, fromStyles.get(key)); + } + }, 1); + + // ending keyframe + builder.keyframe(time, frame -> { + for (String key : targetStyles.keySet()) { + frame.style(key, targetStyles.get(key)); + } + }, 1); + + return builder.build(); + } + public void kill(VAnimation animation) { Long begin = activeAnimations.remove(animation); UnwindContext unwindCtx = unwindingAnimations.remove(animation); From 6f4e770e95e9dc94eba3075dbc3f1034e28bd140 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:16:27 +0200 Subject: [PATCH 472/661] test transition animation --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index c644da96..03ae4837 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -9,6 +9,7 @@ import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.style.animation.LoopMode; import net.snackbag.vera.style.animation.VAnimation; +import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -42,11 +43,14 @@ public void init() { .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); - testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); - //testRect.onHoverLeave(() -> testRect.animations.unwindOrActivateReversed(hoverAnimation)); +// testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); + testRect.onHover(() -> testRect.animations.activateTransition(StyleState.DEFAULT, StyleState.HOVERED, 5000, Easings.LINEAR)); + testRect.onHoverLeave(() -> testRect.animations.activateTransition(StyleState.HOVERED, StyleState.DEFAULT, 5000, Easings.LINEAR)); +// testRect.onHoverLeave(() -> testRect.animations.activate(hoverAnimation)); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); + testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); From 53b42dd7193ae46e0e3f453248226b8663b61ce2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:54:05 +0200 Subject: [PATCH 473/661] make easings identifiable by name --- .../net/snackbag/vera/style/animation/easing/VEasing.java | 7 +++++++ .../vera/style/animation/easing/VLinearEasing.java | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java index 679f6ebe..3e356ba7 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java @@ -1,6 +1,13 @@ package net.snackbag.vera.style.animation.easing; +import net.snackbag.vera.Vera; + public abstract class VEasing { + public VEasing(String name) { + if (Vera.registrar.getEasingIgnoreCase(name) != null) return; + Vera.registrar.registerEasing(name, this); + } + public abstract float apply(float from, float to, float delta); public abstract int apply(int from, int to, float delta); } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java index c1a58818..3d71e655 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java @@ -1,7 +1,9 @@ package net.snackbag.vera.style.animation.easing; public class VLinearEasing extends VEasing { - protected VLinearEasing() {} + protected VLinearEasing() { + super("linear"); + } @Override public float apply(float from, float to, float delta) { From 2c7aa71e29b6fe845dacc350d47329ac7cd037d7 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:54:36 +0200 Subject: [PATCH 474/661] register all easings in the registrar --- .../net/snackbag/mcvera/impl/MCVeraRegistrar.java | 12 ++++++++++++ .../vera/style/animation/easing/Easings.java | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java index d5c9f9f9..68393e3a 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java @@ -1,13 +1,17 @@ package net.snackbag.mcvera.impl; +import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.style.standard.VStandardStyle; import net.snackbag.vera.style.VStyleSheet; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; public class MCVeraRegistrar { private final List standardStyles = new ArrayList<>(); + private final HashMap easings = new HashMap<>(); public void registerStandardStyle(VStandardStyle style) { standardStyles.add(style); @@ -19,4 +23,12 @@ public void applyStandardWidgetStyles(VStyleSheet sheet) { standardStyle.apply(sheet); } } + + public void registerEasing(String name, VEasing easing) { + easings.put(name.toLowerCase(), easing); + } + + public @Nullable VEasing getEasingIgnoreCase(String name) { + return easings.getOrDefault(name.toLowerCase(), null); + } } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java b/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java index a61d46f8..ca2c26dd 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java @@ -1,5 +1,11 @@ package net.snackbag.vera.style.animation.easing; +import net.snackbag.vera.Vera; + public class Easings { public static final VLinearEasing LINEAR = new VLinearEasing(); + + public static VEasing getIgnoreCase(String name) { + return Vera.registrar.getEasingIgnoreCase(name); + } } From 21458afb5ad9bd72d916cb34e6cb95f67b1cce05 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:54:41 +0200 Subject: [PATCH 475/661] add caution message --- src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java index 68393e3a..ca915cf2 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRegistrar.java @@ -9,6 +9,11 @@ import java.util.HashMap; import java.util.List; +/** + * Main Vera registry manager. Use with caution: there are (almost) no + * safety checks. Therefore, it is recommended to use the classes that directly + * implement registrar functionality than touching it yourself. + */ public class MCVeraRegistrar { private final List standardStyles = new ArrayList<>(); private final HashMap easings = new HashMap<>(); From 421998e819b7947d0be97bd9e9079a01c70557de Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:54:49 +0200 Subject: [PATCH 476/661] add easing SVT --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index b615f595..94de7127 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -5,6 +5,7 @@ import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Color; import net.snackbag.vera.core.v4.V4Int; +import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.style.animation.easing.VEasing; import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; @@ -21,6 +22,7 @@ public enum StyleValueType { .withSize(easing.apply(from.getSize(), to.getSize(), delta)) .withName(delta > 0.5 ? to.getName() : from.getName())), CURSOR(VCursorShape.DEFAULT, (f, t, e, d) -> d > 0.5 ? t : f), + EASING(Easings.LINEAR, (f, t, e, d) -> d > 0.5 ? t : f), V4INT(new V4Int(0), (from, to, easing, delta) -> new V4Int( easing.apply(from.get1(), to.get1(), delta), @@ -47,6 +49,7 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { if (val instanceof String s) { if (bias == IDENTIFIER && s.matches("^[\\w-./]*:[\\w-./]*$")) return IDENTIFIER; else if (bias == CURSOR && EnumUtils.getEnumIgnoreCase(VCursorShape.class, s) != null) return CURSOR; + else if (bias == EASING && Easings.getIgnoreCase(s) != null) return EASING; return STRING; } else if (val instanceof V4Color || (bias == V4COLOR && (val instanceof VColor[] || val instanceof VColor))) return V4COLOR; @@ -54,6 +57,7 @@ else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val return V4INT; else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof VCursorShape) return CURSOR; + else if (val instanceof VEasing) return EASING; else if (val instanceof Integer) return INT; else if (val instanceof Float || val instanceof Double) return FLOAT; else if (val instanceof VColor) return COLOR; @@ -65,6 +69,7 @@ public static Object convert(Object value, StyleValueType to) { if (value instanceof String v) { if (to == IDENTIFIER) return new Identifier(v); else if (to == CURSOR) return EnumUtils.getEnumIgnoreCase(VCursorShape.class, v); + else if (to == EASING) return Easings.getIgnoreCase(v); } else if (value instanceof int[] || value instanceof Integer[]) { From 3f529af610bb752675483575c78fd7ecb0e99df3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:55:01 +0200 Subject: [PATCH 477/661] add easing standard styles --- .../snackbag/vera/style/standard/WidgetStandardStyle.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index 7cded930..36bc6a5a 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -6,6 +6,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.widget.VWidget; public class WidgetStandardStyle implements VStandardStyle { @@ -17,6 +18,10 @@ public void apply(VStyleSheet sheet) { // Border sheet.setKey(VWidget.class, "border-color", new V4Color(VColor.black())); sheet.setKey(VWidget.class, "border-size", new V4Int(0)); + + // Transition + sheet.setKey(VWidget.class, "transition", 0); + sheet.setKey(VWidget.class, "transition-easing", Easings.LINEAR); } @Override @@ -25,6 +30,8 @@ public void reserve(VStyleSheet sheet) { sheet.reserveType("cursor", StyleValueType.CURSOR); sheet.reserveType("border-color", StyleValueType.V4COLOR); sheet.reserveType("border-size", StyleValueType.V4INT); + sheet.reserveType("transition", StyleValueType.INT); + sheet.reserveType("transition-easing", StyleValueType.EASING); // TODO: add background-color // TODO: add padding From d33aa65212ad06e550f558459810f13b1a5e0f96 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 16:55:27 +0200 Subject: [PATCH 478/661] use transition style to test --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 03ae4837..6902b6b9 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -44,13 +44,12 @@ public void init() { VRect testRect = new VRect(VColor.black(), this).alsoAdd(); // testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); - testRect.onHover(() -> testRect.animations.activateTransition(StyleState.DEFAULT, StyleState.HOVERED, 5000, Easings.LINEAR)); - testRect.onHoverLeave(() -> testRect.animations.activateTransition(StyleState.HOVERED, StyleState.DEFAULT, 5000, Easings.LINEAR)); // testRect.onHoverLeave(() -> testRect.animations.activate(hoverAnimation)); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); + testRect.setStyle("transition", StyleState.HOVERED, 1000); testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); From a929a910a4562f07a1b698fd4b4346a3b88b0f8a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 17:09:38 +0200 Subject: [PATCH 479/661] add inherits metho --- src/main/java/net/snackbag/vera/style/StyleState.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/StyleState.java index 82efbe00..7c180e1d 100644 --- a/src/main/java/net/snackbag/vera/style/StyleState.java +++ b/src/main/java/net/snackbag/vera/style/StyleState.java @@ -40,4 +40,15 @@ public enum StyleState { this.identifier = identifier; this.fallback = fallback; } + + public boolean inherits(StyleState state) { + StyleState next = this; + + while (next != null) { + if (next == state) return true; + next = next.fallback; + } + + return false; + } } From a7fd747f06d6635ced65a1d22898e4e0fe721872 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 17:10:48 +0200 Subject: [PATCH 480/661] better docstring --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index f7e8a5ff..53f49c9c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -42,7 +42,7 @@ public boolean isRewinding(String name) { } /** - * Unwinds animations whenever they come to their end. + * Unwinds or kills animations whenever they come to their end. * Called in {@link net.snackbag.mcvera.impl.MCVeraRenderer#renderApp(VeraApp)} */ public void update() { From dbbec95564eed02954e8223245e221f530808b71 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 17:12:46 +0200 Subject: [PATCH 481/661] mark codeblock --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 53f49c9c..d50041ab 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -13,7 +13,7 @@ import java.util.Objects; /** - * Per-widget handler for animations. This is after stylesheet.getKey, so there is no differentiation between + * Per-widget handler for animations. This is after stylesheet.getKey, so there is no differentiation between * widget-specific styles, class styles and standard styles. */ public class AnimationEngine { From f0c312811f41f0a7a1daadc6ae65150796151f69 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 25 Oct 2025 17:34:36 +0200 Subject: [PATCH 482/661] prevStyleState -> handledPrevStyleState --- src/main/java/net/snackbag/vera/widget/VWidget.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 2bc8d70f..2a1e9bfb 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -24,7 +24,7 @@ public abstract class VWidget> extends VElement { private boolean leftClickDown = false; private boolean middleClickDown = false; private boolean rightClickDown = false; - private StyleState prevStyleState = StyleState.DEFAULT; + private StyleState handledPrevStyleState = StyleState.DEFAULT; public final AnimationEngine animations = new AnimationEngine(this); public final LinkedHashSet classes = new LinkedHashSet<>(); @@ -293,9 +293,9 @@ public void afterBuiltinEvent(String name, Object... args) { private void updateIfNeeded() { StyleState state = createStyleState(); - if (state != prevStyleState) { + if (state != handledPrevStyleState) { update(); - prevStyleState = state; + handledPrevStyleState = state; } } From 03b35a7d675ee9654bf8d27bda67834f11c226c1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 2 Feb 2026 13:52:21 +0100 Subject: [PATCH 483/661] add after and before render --- src/main/java/net/snackbag/vera/widget/VWidget.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 2a1e9bfb..862137fb 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -24,7 +24,8 @@ public abstract class VWidget> extends VElement { private boolean leftClickDown = false; private boolean middleClickDown = false; private boolean rightClickDown = false; - private StyleState handledPrevStyleState = StyleState.DEFAULT; + private StyleState handledPrevStyleState = StyleState.DEFAULT; // constantly updates + private StyleState prevStyleState = StyleState.DEFAULT; // updates max once per frame, can be seen as the definite result public final AnimationEngine animations = new AnimationEngine(this); public final LinkedHashSet classes = new LinkedHashSet<>(); @@ -139,6 +140,16 @@ public void renderOverlay() { Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), 0, getStyle("overlay", state)); } + public void beforeRender() { + StyleState state = createStyleState(); + + if (state != prevStyleState) { + prevStyleState = state; + } + } + + public void afterRender() {} + public boolean isLeftClickDown() { return leftClickDown; } From aa5fddc889b43e5260e284aed44457bbf54f5c19 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 2 Feb 2026 13:53:04 +0100 Subject: [PATCH 484/661] call before and after --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 5859dd22..984093e8 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -74,6 +74,7 @@ public void renderApp(VeraApp app) { if (widget != hoveredWidget && widget.isHovered()) widget.setHovered(false); else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); + widget.beforeRender(); widget.animations.update(); if (widget.visibilityConditionsPassed()) { @@ -81,6 +82,8 @@ public void renderApp(VeraApp app) { widget.renderBorder(); widget.renderOverlay(); } + + widget.afterRender(); } app.renderAfterWidgets(); From 5970742e90eac235e8667ebf66ff305642b3ed5f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 2 Feb 2026 13:53:11 +0100 Subject: [PATCH 485/661] add demo thing --- .../com/example/demo/DemoApplication.java | 64 +++++++++++++++++++ src/main/java/com/example/demo/DemoMod.java | 7 ++ .../net/snackbag/mcvera/InternalCommands.java | 11 ++++ 3 files changed, 82 insertions(+) create mode 100644 src/main/java/com/example/demo/DemoApplication.java create mode 100644 src/main/java/com/example/demo/DemoMod.java diff --git a/src/main/java/com/example/demo/DemoApplication.java b/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 00000000..e24ea2a3 --- /dev/null +++ b/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,64 @@ +package com.example.demo; + +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.flag.VLayoutAlignmentFlag; +import net.snackbag.vera.layout.VHLayout; +import net.snackbag.vera.layout.VVLayout; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.widget.VLabel; + +public class DemoApplication extends VeraApp { + private int clicks = 0; + private VHLayout layout; + private VVLayout centerLayout; + + @Override + public void init() { + new VShortcut(this, "escape", this::hide); + setBackgroundColor(VColor.black().withOpacity(0.2f)); + + layout = new VHLayout(this, 0, 0); + layout.alignment = VLayoutAlignmentFlag.CENTER; + + centerLayout = new VVLayout(layout); + centerLayout.alignment = VLayoutAlignmentFlag.CENTER; + + VLabel label = new VLabel("Not clicked yet", this).alsoAddTo(centerLayout); + label.modifyFontColor().rgb(VColor.white()); + + VLabel button = new VLabel("Click me", this).alsoAddTo(centerLayout); + + button.modifyFontColor().rgb(VColor.of(95, 180, 0)); + button.setStyle("background-color", VColor.white()); + button.setStyle("padding", 4); + button.setStyle("overlay", StyleState.HOVERED, VColor.white().withOpacity(0.5f)); + button.setStyle("border-size", 1); + button.setStyle("border-color", VColor.of(95, 180, 0)); + button.setStyle("cursor", VCursorShape.POINTING_HAND); + + button.onLeftClick(() -> { + clicks++; + label.setText("Clicks: " + clicks); + label.adjustSize(); + }); + + new VShortcut(this, "leftctrl+d", () -> { + System.out.println("Debug hit"); + }); + } + + @Override + public void update() { + super.update(); + + setWidth(Vera.provider.getScreenWidth()); + setHeight(Vera.provider.getScreenHeight()); + + layout.setSize(getWidth(), getHeight()); + centerLayout.setHeight(getHeight()); + } +} diff --git a/src/main/java/com/example/demo/DemoMod.java b/src/main/java/com/example/demo/DemoMod.java new file mode 100644 index 00000000..3330c9bd --- /dev/null +++ b/src/main/java/com/example/demo/DemoMod.java @@ -0,0 +1,7 @@ +package com.example.demo; + +public class DemoMod { + public static void init() { + new DemoApplication().show(); + } +} diff --git a/src/main/java/net/snackbag/mcvera/InternalCommands.java b/src/main/java/net/snackbag/mcvera/InternalCommands.java index ac16ce0d..1e49e148 100644 --- a/src/main/java/net/snackbag/mcvera/InternalCommands.java +++ b/src/main/java/net/snackbag/mcvera/InternalCommands.java @@ -1,10 +1,12 @@ package net.snackbag.mcvera; +import com.example.demo.DemoMod; import com.mojang.brigadier.CommandDispatcher; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.command.CommandRegistryAccess; +import net.snackbag.mcvera.test.LayoutCenteringTestApplication; import net.snackbag.mcvera.test.LayoutTestApplication; import net.snackbag.mcvera.test.StyleTestApplication; import net.snackbag.mcvera.test.TestApplication; @@ -31,12 +33,21 @@ public static void register( LayoutTestApplication.INSTANCE.show(); return 1; })) + .then(ClientCommandManager.literal("layoutalignments").executes((ctx) -> { + LayoutCenteringTestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("demo").executes(ctx -> { + DemoMod.init(); + return 1; + })) ) .then(ClientCommandManager.literal("clear-tests") .executes((ctx) -> { TestApplication.INSTANCE = new TestApplication(); StyleTestApplication.INSTANCE = new StyleTestApplication(); LayoutTestApplication.INSTANCE = new LayoutTestApplication(); + LayoutCenteringTestApplication.INSTANCE = new LayoutCenteringTestApplication(); return 1; }) ) From 88a62b05a164b0d589c65d36c6ec3bcbf9189751 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 2 Feb 2026 13:53:27 +0100 Subject: [PATCH 486/661] final before total rewrite of animation system --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 6902b6b9..4536b43b 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -44,12 +44,11 @@ public void init() { VRect testRect = new VRect(VColor.black(), this).alsoAdd(); // testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); -// testRect.onHoverLeave(() -> testRect.animations.activate(hoverAnimation)); +// testRect.onHoverLeave(() -> testRect.animations.unwind(hoverAnimation)); testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); - testRect.setStyle("transition", StyleState.HOVERED, 1000); testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); From e15655bb0cc547fdf305ca6047cfc7a708bee6d0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Feb 2026 22:33:27 +0100 Subject: [PATCH 487/661] begin animation engine --- .../java/net/snackbag/vera/core/VeraApp.java | 11 - .../vera/style/animation/AnimationEngine.java | 249 ------------------ .../vera/style/animation/LoopMode.java | 6 - .../vera/style/animation/VAnimation.java | 235 ----------------- .../vera/style/animation/VKeyframe.java | 40 --- .../vera/style/animation/VeraPipeline.java | 54 ---- .../composite/AnimationComposite.java | 61 ----- .../style/animation/composite/Composite.java | 26 -- .../animation/composite/WindingComposite.java | 70 ----- .../net/snackbag/vera/widget/VWidget.java | 10 +- 10 files changed, 6 insertions(+), 756 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/LoopMode.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/VAnimation.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/VKeyframe.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/Composite.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 747e5554..ebe5dfd9 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -7,9 +7,6 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; -import net.snackbag.vera.style.animation.VeraPipeline; -import net.snackbag.vera.style.animation.composite.AnimationComposite; -import net.snackbag.vera.style.animation.composite.WindingComposite; import net.snackbag.vera.util.Geometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -19,7 +16,6 @@ public abstract class VeraApp { public final VStyleSheet styleSheet = new VStyleSheet(); - public final VeraPipeline pipeline = new VeraPipeline(this); private final List> widgets; private final HashMap shortcuts; @@ -59,13 +55,6 @@ public VeraApp(boolean mouseRequired) { this.visible = false; setPositioning(VWindowPositioningFlag.SCREEN); - - loadComposites(); - } - - public void loadComposites() { - pipeline.addPass(new AnimationComposite()); - pipeline.addPass(new WindingComposite()); } public void setCursorVisible(boolean cursorVisible) { diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java deleted file mode 100644 index d50041ab..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ /dev/null @@ -1,249 +0,0 @@ -package net.snackbag.vera.style.animation; - -import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; -import net.snackbag.vera.style.StyleState; -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.easing.VEasing; -import net.snackbag.vera.widget.VWidget; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Objects; - -/** - * Per-widget handler for animations. This is after stylesheet.getKey, so there is no differentiation between - * widget-specific styles, class styles and standard styles. - */ -public class AnimationEngine { - public final VWidget widget; - private final HashMap activeAnimations = new HashMap<>(); - private final HashMap unwindingAnimations = new HashMap<>(); - private final HashMap rewindingAnimations = new HashMap<>(); - - private long cacheId = 0; - private final HashMap cache = new HashMap<>(); - - public AnimationEngine(VWidget widget) { - this.widget = widget; - } - - public boolean isActive(String name) { - return activeAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); - } - - public boolean isUnwinding(String name) { - return unwindingAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); - } - - public boolean isRewinding(String name) { - return rewindingAnimations.keySet().stream().anyMatch(anim -> anim.name.equals(name)); - } - - /** - * Unwinds or kills animations whenever they come to their end. - * Called in {@link net.snackbag.mcvera.impl.MCVeraRenderer#renderApp(VeraApp)} - */ - public void update() { - final long time = System.currentTimeMillis(); - - HashMap animations = (HashMap) activeAnimations.clone(); - for (VAnimation animation : animations.keySet()) { - if ((time - getTimeSinceActive(animation) >= animation.getTotalTime() - animation.unwindTime) && animation.autoUnwindAtEnd) { - unwind(animation); - } - - if (time - getTimeSinceActive(animation) >= animation.getTotalTime() && animation.loopMode == LoopMode.NONE) { - widget.events.fire(Events.Animation.FINISH, animation, getTimeSinceActive(animation)); - - if (animation.writeFinalStateTarget != null) { - HashMap finalStyles = animation.getFinalStyles(); - for (String key : finalStyles.keySet()) { - widget.setStyle(key, animation.writeFinalStateTarget, finalStyles.get(key)); - } - } - - kill(animation); - } - } - } - - public HashMap getAffectedStyles(StyleState state) { - return widget.app.styleSheet.getResolvedKeys(widget, state); - } - - public void activate(VAnimation animation) { - activate(animation, false); - } - - public void activate(VAnimation animation, boolean override) { - if (!override && isActive(animation.name)) return; - activeAnimations.put(animation, System.currentTimeMillis()); - widget.events.fire(Events.Animation.BEGIN, animation); - } - - public void activateOrRewind(VAnimation animation) { - if (isUnwinding(animation.name)) rewind(animation); - else activate(animation); - } - - public void activateTransition(StyleState from, StyleState target, int time, VEasing easing) { - activate(createTransitionAnimation(from, target, time, easing)); - } - - public VAnimation createTransitionAnimation(StyleState from, StyleState target, int time, VEasing easing) { - VAnimation.Builder builder = new VAnimation.Builder(widget.app, VAnimation.INTERNAL_TRANSITION_ANIMATION_NAME); - - builder.unwindEasing(easing); - builder.unwindTime(time); - - HashMap fromStyles = getAffectedStyles(from); - HashMap targetStyles = getAffectedStyles(target); - fromStyles.forEach(targetStyles::putIfAbsent); - targetStyles.forEach(fromStyles::putIfAbsent); - - // beginning keyframe - builder.keyframe(0, frame -> { - for (String key : fromStyles.keySet()) { - frame.style(key, fromStyles.get(key)); - } - }, 1); - - // ending keyframe - builder.keyframe(time, frame -> { - for (String key : targetStyles.keySet()) { - frame.style(key, targetStyles.get(key)); - } - }, 1); - - return builder.build(); - } - - public void kill(VAnimation animation) { - Long begin = activeAnimations.remove(animation); - UnwindContext unwindCtx = unwindingAnimations.remove(animation); - RewindContext rewindCtx = rewindingAnimations.remove(animation); - - Long unwindBegun = unwindCtx != null ? unwindCtx.begun : null; - Long rewindBegun = rewindCtx != null ? rewindCtx.begun : null; - - Long nullableBegun = Vera.firstOf(Objects::nonNull, begin, unwindBegun, rewindBegun); - - if (nullableBegun != null) widget.events.fire(Events.Animation.FINISH, animation, nullableBegun); - } - - public void unwind(VAnimation animation) { - unwind(animation, false); - } - - public void unwind(VAnimation animation, boolean override) { - if (!isActive(animation.name)) return; - if (!override && isUnwinding(animation.name)) return; - - long time = System.currentTimeMillis(); - widget.events.fire(Events.Animation.UNWIND_BEGIN, animation); - - int rewindProgress = 0; - if (rewindingAnimations.containsKey(animation)) { - RewindContext rewindCtx = rewindingAnimations.remove(animation); - int previousUnwindProgress = rewindCtx.unwindProgress(); - int currentRewindTime = (int) (time - rewindCtx.begun()); - - rewindProgress = Math.max(0, previousUnwindProgress - currentRewindTime); - } - - unwindingAnimations.put(animation, new UnwindContext(time, rewindProgress)); - } - - public void rewind(VAnimation animation) { - rewind(animation, false); - } - - public void rewind(VAnimation animation, boolean override) { - if (!isActive(animation.name)) return; - if (!unwindingAnimations.containsKey(animation)) return; - if (!override && isRewinding(animation.name)) return; - - long time = System.currentTimeMillis(); - widget.events.fire(Events.Animation.REWIND_BEGIN, animation); - - UnwindContext unwindCtx = unwindingAnimations.remove(animation); - int totalUnwindProgress = unwindCtx.rewindProgress() + (int) (time - unwindCtx.begun()); - - rewindingAnimations.put(animation, new RewindContext(time, totalUnwindProgress)); - } - - public @Nullable VAnimation getIfEverActive(String name) { - return activeAnimations.keySet() - .stream() - .filter(anim -> anim.name.equals(name)) - .findFirst() - .orElse(null); - } - - public VAnimation[] getAllActive() { - return activeAnimations.keySet().toArray(new VAnimation[0]); - } - - public VAnimation[] getAllUnwinding() { - return unwindingAnimations.keySet().toArray(new VAnimation[0]); - } - - public VAnimation[] getAllRewinding() { - return rewindingAnimations.keySet().toArray(new VAnimation[0]); - } - - public void checkCache() { - if (cacheId != Vera.renderCacheId) { - cache.clear(); - cacheId = Vera.renderCacheId; - } - } - - public T animateStyle(String style, T value) { - checkCache(); - - if (value == null) return null; - if (!cache.containsKey(style)) { - StyleValueType type = StyleValueType.get(value, null); - cache.put(style, widget.app.pipeline.applyComposites(this, style, type, value)); - } - - return (T) cache.get(style); - } - - /** - * Gets the time an animation has been active since, if it isn't active at all it will return -1 - * - * @param animation the animation to check - * @return when the animation was started - */ - public long getTimeSinceActive(VAnimation animation) { - return activeAnimations.getOrDefault(animation, -1L); - } - - public @Nullable UnwindContext getUnwindContext(VAnimation animation) { - return unwindingAnimations.getOrDefault(animation, null); - } - - public @Nullable RewindContext getRewindContext(VAnimation animation) { - return rewindingAnimations.getOrDefault(animation, null); - } - - /** - * Context for animation rewinding - * - * @param begun the timestamp when the animation started rewinding - * @param unwindProgress the amount of milliseconds the animation has already been unwinding - */ - public record RewindContext(Long begun, int unwindProgress) {} - - /** - * Context for animation unwinding - * - * @param begun the timestamp when the animation started unwinding - * @param rewindProgress the amount of milliseconds the animation has already been rewinding - */ - public record UnwindContext(Long begun, int rewindProgress) {} -} diff --git a/src/main/java/net/snackbag/vera/style/animation/LoopMode.java b/src/main/java/net/snackbag/vera/style/animation/LoopMode.java deleted file mode 100644 index 0e998196..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/LoopMode.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.snackbag.vera.style.animation; - -public enum LoopMode { - NONE, - REPEAT -} diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java deleted file mode 100644 index b2e250e7..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ /dev/null @@ -1,235 +0,0 @@ -package net.snackbag.vera.style.animation; - -import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.StyleState; -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.easing.Easings; -import net.snackbag.vera.style.animation.easing.VEasing; -import org.jetbrains.annotations.Nullable; -import oshi.util.tuples.Pair; - -import java.util.*; -import java.util.function.Consumer; - -/** - * Represents an animation template scoped to a specific {@link VeraApp} instance. - *

- * Animations are app-specific and should not be shared across multiple apps. - * If you need to reuse an animation in different apps, define a method that - * generates a new instance of the animation for each target app. - */ -public class VAnimation { - public static final String INTERNAL_TRANSITION_ANIMATION_NAME = "vera-transition"; - - public final String name; - public final VeraApp app; - - public final int unwindTime; - public final boolean autoUnwindAtEnd; - public final VEasing unwindEasing; - public final LoopMode loopMode; - public final @Nullable StyleState writeFinalStateTarget; - - private final List keyframes = new ArrayList<>(); - protected final HashMap styleAffections = new HashMap<>(); - - private int totalTime = 0; - - public VAnimation( - String name, - int unwindTime, boolean autoUnwindAtEnd, VEasing unwindEasing, - LoopMode loopMode, @Nullable StyleState writeFinalStateTarget, - VeraApp app - ) { - this.name = name; - this.unwindTime = unwindTime; - this.autoUnwindAtEnd = autoUnwindAtEnd; - this.unwindEasing = unwindEasing; - this.totalTime = autoUnwindAtEnd ? unwindTime : 0; - this.loopMode = loopMode; - this.writeFinalStateTarget = writeFinalStateTarget; - - this.app = app; - } - - public void addKeyframe(VKeyframe keyframe) { - this.keyframes.add(keyframe); - keyframe.animation.set(this); - totalTime += keyframe.cumulatedTime; - } - - public boolean affects(String style) { - return styleAffections.containsKey(style); - } - - public @Nullable T calculateStyle(String style, T original, StyleValueType svt, long timeSinceActive, boolean addMonotoneEnd) { - // TODO: implement loop modes - - if (!affects(style)) return null; - - int margin = 0; - for (int i = 0; i < keyframes.size(); i++) { - VKeyframe frame = keyframes.get(i); - - if (timeSinceActive >= margin && timeSinceActive <= margin + frame.cumulatedTime) { - int timeInFrame = (int) timeSinceActive - margin; - boolean isTransition = timeInFrame <= frame.transitionTime; - - if (!isTransition) return (T) frame.styles.get(style).getB(); - - T before; - T after = getStyleForKeyframeDeep(i, style); - - if (i > 0) before = getStyleForKeyframeDeep(i - 1, style); - else before = original; - - float progress = timeInFrame / (float) frame.transitionTime; - - return (T) svt.animationTransition.apply(before, after, frame.easeIn, progress); - } - - margin += frame.cumulatedTime; - } - - if (addMonotoneEnd && timeSinceActive >= totalTime - unwindTime && timeSinceActive <= totalTime) { - return (T) keyframes.get(keyframes.size() - 1).styles.get(style).getB(); - } - - return original; - } - - public T getStyleForKeyframeDeep(int targetIndex, String style) { - VKeyframe target = keyframes.get(targetIndex); - - for (int i = targetIndex; !target.styles.containsKey(style); i--) { - target = keyframes.get(i - 1); - } - - Pair pair = target.styles.get(style); - return (T) StyleValueType.convert(pair.getB(), pair.getA()); - } - - public HashMap getFinalStyles() { - HashMap finalStyles = new HashMap<>(); - - if (keyframes.isEmpty()) return finalStyles; - for (String style : styleAffections.keySet()) { - finalStyles.put(style, getStyleForKeyframeDeep(keyframes.size() - 1, style)); - } - - return finalStyles; - } - - public int getTotalTime() { - return totalTime; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof VAnimation animation)) return false; - return Objects.equals(name, animation.name) && Objects.equals(app, animation.app); - } - - @Override - public int hashCode() { - return Objects.hash(name, app); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append(" (" + totalTime + "ms) {"); - builder.append("\n"); - - for (VKeyframe frame : keyframes) { - builder.append(" -> "); - builder.append(frame.transitionTime); - builder.append("ms -- "); - builder.append(frame.stayTime); - builder.append("ms: "); - builder.append(frame.easeIn); - builder.append("\n"); - - - for (String style : frame.styles.keySet()) { - Pair val = frame.styles.get(style); - - builder.append(" "); - builder.append(style); - builder.append(": "); - builder.append(val.getB()); - builder.append(" ("); - builder.append(val.getA()); - builder.append(")\n"); - } - } - - builder.append("}"); - - return builder.toString(); - } - - public VKeyframe[] getKeyframes() { - return keyframes.toArray(new VKeyframe[0]); - } - - public static class Builder { - private final String name; - private final VeraApp app; - - private LoopMode loopMode = LoopMode.NONE; - private @Nullable StyleState keepFinalStyle = null; - private int unwindTime = 0; - private boolean autoUnwindAtEnd = false; - private VEasing unwindEasing = Easings.LINEAR; - - private final List>> keyframes = new ArrayList<>(); - - public Builder(VeraApp app, String name) { - this.name = name; - this.app = app; - } - - public Builder loop(LoopMode mode) { - this.loopMode = mode; - return this; - } - - public Builder unwindTime(int ms) { - this.unwindTime = ms; - return this; - } - - public Builder unwindOnFinish() { - this.autoUnwindAtEnd = true; - return this; - } - - public Builder unwindEasing(VEasing easing) { - this.unwindEasing = easing; - return this; - } - - public Builder keepFinalStyle(StyleState writeTo) { - this.keepFinalStyle = writeTo; - return this; - } - - public Builder keyframe(int transitionMs, Consumer frame, int stayMs) { - keyframes.add(new Pair<>(new VKeyframe(transitionMs, stayMs), frame)); - return this; - } - - public VAnimation build() { - VAnimation animation = new VAnimation(name, unwindTime, autoUnwindAtEnd, unwindEasing, loopMode, keepFinalStyle, app); - - for (Pair> frame : keyframes) { - animation.addKeyframe(frame.getA()); - frame.getB().accept(frame.getA()); - } - - return animation; - } - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java deleted file mode 100644 index 8cc182ac..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ /dev/null @@ -1,40 +0,0 @@ -package net.snackbag.vera.style.animation; - -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.easing.Easings; -import net.snackbag.vera.style.animation.easing.VEasing; -import net.snackbag.vera.util.Once; -import org.jetbrains.annotations.NotNull; -import oshi.util.tuples.Pair; - -import java.util.HashMap; - -public class VKeyframe { - protected final Once animation = new Once<>(); - protected final int transitionTime; - protected final int stayTime; - protected final int cumulatedTime; - protected final HashMap> styles = new HashMap<>(); - - public @NotNull VEasing easeIn = Easings.LINEAR; - - public VKeyframe(int transitionTime, int stayTime) { - this.transitionTime = transitionTime; - this.stayTime = stayTime; - this.cumulatedTime = this.transitionTime + this.stayTime; - } - - public void style(String key, Object value) { - StyleValueType reservation = animation.get().app.styleSheet.getReservation(key); - if (reservation == null) throw new UnsupportedOperationException("Cannot set keyframe style to unreserved style key"); - - animation.get().styleAffections.merge(key, 1, Integer::sum); - - Object converted = StyleValueType.convert(value, reservation); - styles.put(key, new Pair<>(reservation, converted)); - } - - public boolean affects(String style) { - return styles.containsKey(style); - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java b/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java deleted file mode 100644 index c1e42e5c..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/VeraPipeline.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.snackbag.vera.style.animation; - -import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.composite.Composite; -import net.snackbag.vera.widget.VWidget; - -import java.util.ArrayList; -import java.util.List; - -public class VeraPipeline { - public final VeraApp app; - private final List passes = new ArrayList<>(); - private final List> cleanWidgets = new ArrayList<>(); - - public VeraPipeline(VeraApp app) { - this.app = app; - } - - public void addPass(Composite pass) { - setupPass(pass); - this.passes.add(pass); - } - - public void addPass(int index, Composite pass) { - setupPass(pass); - this.passes.add(index, pass); - } - - private void setupPass(Composite pass) { - boolean unique = pass.pipeline.setSafe(this); - if (!unique) throw new UnsupportedOperationException("Cannot setup composite pass if composite is already bound to pipeline"); - } - - public T applyComposites(AnimationEngine engine, String style, StyleValueType type, T in) { - Composite.Context ctx = new Composite.Context(engine,style, type, in); - boolean isNewFrame = false; - - for (Composite pass : passes) { - if (pass.frameTime != Vera.renderCacheId) { - pass.frameTime = Vera.renderCacheId; - cleanWidgets.clear(); - pass.generateUniforms(); - isNewFrame = true; - } - - if (!cleanWidgets.contains(engine.widget)) pass.applyWidget(engine.widget); - in = pass.applyStyle(ctx, in, isNewFrame); - } - - return in; - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java deleted file mode 100644 index b5d6a38b..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/composite/AnimationComposite.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.snackbag.vera.style.animation.composite; - -import net.snackbag.vera.style.animation.AnimationEngine; -import net.snackbag.vera.style.animation.VAnimation; -import net.snackbag.vera.widget.VWidget; - -import java.util.HashMap; - -public class AnimationComposite extends Composite { - private final HashMap animations = new HashMap<>(); - private final HashMap animationTimes = new HashMap<>(); - private long time; // so we don't have to call it again - - @Override - public void generateUniforms() { - animations.clear(); - animationTimes.clear(); - time = System.currentTimeMillis(); - } - - @Override - public void applyWidget(VWidget widget) { - VAnimation[] active = widget.animations.getAllActive(); - - // Only store if we have active animations - if (active.length > 0) { - animations.put(widget.animations, active); - - for (VAnimation animation : active) { - animationTimes.put(animation, time - widget.animations.getTimeSinceActive(animation)); - } - } - } - - @Override - public T applyStyle(Context ctx, T in, boolean isNewFrame) { - // Most caching isn't necessary, since it's done earlier. - - AnimationEngine engine = ctx.engine(); - VAnimation[] engineAnimations = animations.get(engine); - - // early return - if (engineAnimations == null || engineAnimations.length == 0) return in; - - T out = in; - for (VAnimation animation : engineAnimations) { - // No need to check affects, since calculateStyle does this internally - T rv = animation.calculateStyle( - ctx.style(), - ctx.original(), - ctx.type(), - animationTimes.get(animation), - animation.autoUnwindAtEnd - ); - - if (rv != null) out = rv; - } - - return out; - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java b/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java deleted file mode 100644 index 160ed1b4..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/composite/Composite.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.snackbag.vera.style.animation.composite; - -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.animation.AnimationEngine; -import net.snackbag.vera.style.animation.VeraPipeline; -import net.snackbag.vera.util.Once; -import net.snackbag.vera.widget.VWidget; - -public abstract class Composite { - public Once pipeline = new Once<>(); - public long frameTime = 0; - - /** - * This method is called per-frame and is entirely independent of the given style. Hence, the name uniform. - */ - public void generateUniforms() {} - - public void applyWidget(VWidget widget) {} - - public T applyStyle(Context ctx, T in, boolean isNewFrame) { - return in; - } - - public record Context(AnimationEngine engine, String style, StyleValueType type, T original) { - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java b/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java deleted file mode 100644 index ee9159ad..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/composite/WindingComposite.java +++ /dev/null @@ -1,70 +0,0 @@ -package net.snackbag.vera.style.animation.composite; - -import net.snackbag.vera.style.animation.AnimationEngine; -import net.snackbag.vera.style.animation.VAnimation; -import net.snackbag.vera.widget.VWidget; - -import java.util.HashMap; - -public class WindingComposite extends Composite { - private final HashMap unwindingAnimations = new HashMap<>(); - private final HashMap rewindingAnimations = new HashMap<>(); - private long time; - - @Override - public void generateUniforms() { - time = System.currentTimeMillis(); - unwindingAnimations.clear(); - rewindingAnimations.clear(); - } - - @Override - public void applyWidget(VWidget widget) { - unwindingAnimations.put(widget.animations, widget.animations.getAllUnwinding()); - rewindingAnimations.put(widget.animations, widget.animations.getAllRewinding()); - } - - @Override - public T applyStyle(Context ctx, T in, boolean isNewFrame) { - AnimationEngine engine = ctx.engine(); - VAnimation[] unwinding = unwindingAnimations.get(engine); - VAnimation[] rewinding = rewindingAnimations.get(engine); - - T out = in; - T original = ctx.original(); - String style = ctx.style(); - - for (VAnimation animation : unwinding) { - if (!animation.affects(style)) continue; - if (animation.unwindTime <= 0) { - out = original; - continue; - } - - AnimationEngine.UnwindContext unwindCtx = engine.getUnwindContext(animation); - int totalProgress = unwindCtx.rewindProgress() + (int) (time - unwindCtx.begun()); - float delta = Math.min((float) totalProgress / (float) animation.unwindTime, 1f); - - out = (T) ctx.type().animationTransition.apply(in, original, animation.unwindEasing, delta); - } - - for (VAnimation animation : rewinding) { - if (!animation.affects(style)) continue; - if (animation.unwindTime <= 0) { - out = in; - continue; - } - - AnimationEngine.RewindContext rewindCtx = engine.getRewindContext(animation); - int currentRewindTime = (int) (time - rewindCtx.begun()); - - // calculate reverse: start from how much we had unwound, go back towards 0 - int remainingUnwindProgress = Math.max(0, rewindCtx.unwindProgress() - currentRewindTime); - float delta = Math.min((float) remainingUnwindProgress / (float) animation.unwindTime, 1f); - - out = (T) ctx.type().animationTransition.apply(in, original, animation.unwindEasing, delta); - } - - return out; - } -} diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 862137fb..c8aa7c55 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -8,8 +8,6 @@ import net.snackbag.vera.event.*; import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; -import net.snackbag.vera.style.animation.AnimationEngine; -import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.util.DragHandler; import java.nio.file.Path; @@ -27,7 +25,6 @@ public abstract class VWidget> extends VElement { private StyleState handledPrevStyleState = StyleState.DEFAULT; // constantly updates private StyleState prevStyleState = StyleState.DEFAULT; // updates max once per frame, can be seen as the definite result - public final AnimationEngine animations = new AnimationEngine(this); public final LinkedHashSet classes = new LinkedHashSet<>(); public VWidget(int x, int y, int width, int height, VeraApp app) { @@ -83,6 +80,10 @@ public V getStyleOrDefault(String key, V dflt, StyleState state) { } public StyleState createStyleState() { + return createStyleState(true); + } + + public StyleState createStyleState(boolean respectAnimationLocks) { // Clicks first if (leftClickDown) return StyleState.LEFT_CLICKED; else if (middleClickDown) return StyleState.MIDDLE_CLICKED; @@ -148,7 +149,8 @@ public void beforeRender() { } } - public void afterRender() {} + public void afterRender() { + } public boolean isLeftClickDown() { return leftClickDown; From e03aeb627ceeea84955d8e11dafac4e2fc57a512 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 13 Feb 2026 23:57:28 +0100 Subject: [PATCH 488/661] make rect follow initial background color --- src/main/java/net/snackbag/vera/widget/VRect.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 7d284b0a..935de528 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -10,6 +10,7 @@ public VRect(VColor color, VeraApp app) { super(0, 0, 20, 20, app); this.focusOnClick = false; + setStyle("background-color", color); } @Override From 1ec294f7feabb8c93b7ea6904d2c13244bb093ab Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:50:39 +0100 Subject: [PATCH 489/661] Easings -> VEasings --- .../vera/style/animation/easing/{Easings.java => VEasings.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/net/snackbag/vera/style/animation/easing/{Easings.java => VEasings.java} (91%) diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java similarity index 91% rename from src/main/java/net/snackbag/vera/style/animation/easing/Easings.java rename to src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index ca2c26dd..ba9e5f2e 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/Easings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -2,7 +2,7 @@ import net.snackbag.vera.Vera; -public class Easings { +public class VEasings { public static final VLinearEasing LINEAR = new VLinearEasing(); public static VEasing getIgnoreCase(String name) { From 45bfb009272ef5c2f25c96b83345e44fad824b80 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:57:38 +0100 Subject: [PATCH 490/661] Events -> VEvents --- .../snackbag/mcvera/impl/MCVeraProvider.java | 7 +-- .../net/snackbag/mcvera/mixin/MouseMixin.java | 4 +- .../mcvera/mixin/ParentElementMixin.java | 16 ++--- src/main/java/net/snackbag/vera/VElement.java | 10 +-- .../java/net/snackbag/vera/core/VeraApp.java | 14 ++--- .../vera/event/{Events.java => VEvents.java} | 2 +- .../net/snackbag/vera/layout/VLayout.java | 8 +-- .../net/snackbag/vera/util/DragHandler.java | 8 +-- .../net/snackbag/vera/widget/VCheckBox.java | 8 +-- .../net/snackbag/vera/widget/VDropdown.java | 24 +++---- .../net/snackbag/vera/widget/VLineInput.java | 62 +++++++++---------- .../net/snackbag/vera/widget/VTabWidget.java | 48 +++++++------- .../net/snackbag/vera/widget/VWidget.java | 56 ++++++++--------- 13 files changed, 133 insertions(+), 134 deletions(-) rename src/main/java/net/snackbag/vera/event/{Events.java => VEvents.java} (99%) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index 1555088a..a27e35ef 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -1,13 +1,12 @@ package net.snackbag.mcvera.impl; import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; import net.snackbag.mcvera.MCVeraData; import net.snackbag.mcvera.screen.VeraVisibilityScreen; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.widget.VWidget; @@ -131,7 +130,7 @@ public void handleFilesDropped(List paths) { if (top != null && top.isPointOverThis(x, y)) { VWidget widget = top.getTopWidgetAt(x, y); if (widget != null) { - widget.events.fire(Events.Widget.FILES_DROPPED, paths); + widget.events.fire(VEvents.Widget.FILES_DROPPED, paths); return; } } @@ -143,7 +142,7 @@ public void handleFilesDropped(List paths) { VWidget widget = app.getTopWidgetAt(x, y); if (widget != null) { - widget.events.fire(Events.Widget.FILES_DROPPED, paths); + widget.events.fire(VEvents.Widget.FILES_DROPPED, paths); didSomething.set(true); } }); diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index 507c34a4..199fb5a8 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -5,7 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; @@ -36,7 +36,7 @@ public abstract class MouseMixin { if (app.isRequiresHierarchy() && app != top) return; VWidget widget = app.getTopWidgetAt(mouseX, mouseY); - if (widget != null) widget.events.fire(Events.Widget.MOUSE_MOVE, mouseX, mouseY); + if (widget != null) widget.events.fire(VEvents.Widget.MOUSE_MOVE, mouseX, mouseY); else if (app.getCursorShape() != VCursorShape.DEFAULT) app.setCursorShape(VCursorShape.DEFAULT); }); diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 1fb85bee..a3fd8c3f 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -5,7 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VMouseButton; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -57,9 +57,9 @@ private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) if (widget == null) return; switch (button) { - case LEFT -> widget.events.fire(Events.Widget.LEFT_CLICK); - case RIGHT -> widget.events.fire(Events.Widget.RIGHT_CLICK); - case MIDDLE -> widget.events.fire(Events.Widget.MIDDLE_CLICK); + case LEFT -> widget.events.fire(VEvents.Widget.LEFT_CLICK); + case RIGHT -> widget.events.fire(VEvents.Widget.RIGHT_CLICK); + case MIDDLE -> widget.events.fire(VEvents.Widget.MIDDLE_CLICK); } DragHandler.down(button, widget); @@ -88,9 +88,9 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto if (widget == null) return; switch (button) { - case LEFT -> widget.events.fire(Events.Widget.LEFT_CLICK_RELEASE); - case RIGHT -> widget.events.fire(Events.Widget.RIGHT_CLICK_RELEASE); - case MIDDLE -> widget.events.fire(Events.Widget.MIDDLE_CLICK_RELEASE); + case LEFT -> widget.events.fire(VEvents.Widget.LEFT_CLICK_RELEASE); + case RIGHT -> widget.events.fire(VEvents.Widget.RIGHT_CLICK_RELEASE); + case MIDDLE -> widget.events.fire(VEvents.Widget.MIDDLE_CLICK_RELEASE); } } @@ -111,6 +111,6 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto @Unique private void handleScrollEvents(@Nullable VWidget widget, int x, int y, double amount) { if (widget == null) return; - widget.events.fire(Events.Widget.SCROLL, x, y, amount); + widget.events.fire(VEvents.Widget.SCROLL, x, y, amount); } } diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 8ac7b39e..b644941d 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -2,7 +2,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.EventHandler; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VWidgetMessageEvent; import net.snackbag.vera.layout.VLayout; import org.jetbrains.annotations.Nullable; @@ -72,19 +72,19 @@ public void show() { // public void onMessage(VWidgetMessageEvent executor) { - events.register(Events.Element.MESSAGE,args -> executor.run((VWidgetMessageEvent.Context) args[0])); + events.register(VEvents.Element.MESSAGE, args -> executor.run((VWidgetMessageEvent.Context) args[0])); } public void sendMessage(VElement element, String type, @Nullable Object content) { - element.events.fire(Events.Element.MESSAGE, new VWidgetMessageEvent.Context(this, type, content)); + element.events.fire(VEvents.Element.MESSAGE, new VWidgetMessageEvent.Context(this, type, content)); } public void onLayoutSwap(Consumer executor) { - events.register(Events.Element.LAYOUT_SWAP, args -> executor.accept((VLayout) args[0])); + events.register(VEvents.Element.LAYOUT_SWAP, args -> executor.accept((VLayout) args[0])); } public void onLayoutRemove(Runnable executor) { - events.register(Events.Element.LAYOUT_REMOVE, args -> executor.run()); + events.register(VEvents.Element.LAYOUT_REMOVE, args -> executor.run()); } // diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index ebe5dfd9..24e4476e 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -3,7 +3,7 @@ import net.minecraft.client.MinecraftClient; import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; @@ -212,10 +212,10 @@ public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; if (isFocusedWidget(widget)) setFocusedWidget(null); - if (widget.isLeftClickDown()) widget.events.fire(Events.Widget.LEFT_CLICK_RELEASE); - if (widget.isMiddleClickDown()) widget.events.fire(Events.Widget.MIDDLE_CLICK_RELEASE); - if (widget.isRightClickDown()) widget.events.fire(Events.Widget.RIGHT_CLICK_RELEASE); - if (widget.isHovered()) widget.events.fire(Events.Widget.HOVER_LEAVE); + if (widget.isLeftClickDown()) widget.events.fire(VEvents.Widget.LEFT_CLICK_RELEASE); + if (widget.isMiddleClickDown()) widget.events.fire(VEvents.Widget.MIDDLE_CLICK_RELEASE); + if (widget.isRightClickDown()) widget.events.fire(VEvents.Widget.RIGHT_CLICK_RELEASE); + if (widget.isHovered()) widget.events.fire(VEvents.Widget.HOVER_LEAVE); this.widgets.remove(widget); } @@ -284,8 +284,8 @@ public void setFocusedWidget(@Nullable VWidget widget) { VWidget oldWidget = this.focusedWidget; this.focusedWidget = widget; - if (oldWidget != null) oldWidget.events.fire(Events.Widget.FOCUS_STATE_CHANGE); - if (widget != null) widget.events.fire(Events.Widget.FOCUS_STATE_CHANGE); + if (oldWidget != null) oldWidget.events.fire(VEvents.Widget.FOCUS_STATE_CHANGE); + if (widget != null) widget.events.fire(VEvents.Widget.FOCUS_STATE_CHANGE); } } diff --git a/src/main/java/net/snackbag/vera/event/Events.java b/src/main/java/net/snackbag/vera/event/VEvents.java similarity index 99% rename from src/main/java/net/snackbag/vera/event/Events.java rename to src/main/java/net/snackbag/vera/event/VEvents.java index f79f09a0..2b6da3d5 100644 --- a/src/main/java/net/snackbag/vera/event/Events.java +++ b/src/main/java/net/snackbag/vera/event/VEvents.java @@ -1,7 +1,7 @@ package net.snackbag.vera.event; // Sorted by :sparkle: the feeling that it looks nice :sparkle: -public class Events { +public class VEvents { // Animation public static class Animation { public static final String BEGIN = "animation-begin"; diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index c7943d8d..34e84df7 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -3,7 +3,7 @@ import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.flag.VLayoutAlignmentFlag; import org.joml.Vector2i; @@ -71,18 +71,18 @@ public int calculateElementsWidth() { public void addElement(VElement elem) { if (elements.contains(elem)) return; elements.add(elem); - elem.events.fire(Events.Element.LAYOUT_SWAP, this); + elem.events.fire(VEvents.Element.LAYOUT_SWAP, this); } public boolean removeElement(VElement elem) { if (!elements.contains(elem)) return false; - elem.events.fire(Events.Element.LAYOUT_REMOVE); + elem.events.fire(VEvents.Element.LAYOUT_REMOVE); return elements.remove(elem); } public void clear() { - for (VElement elem : elements) elem.events.fire(Events.Element.LAYOUT_REMOVE); + for (VElement elem : elements) elem.events.fire(VEvents.Element.LAYOUT_REMOVE); elements.clear(); } } diff --git a/src/main/java/net/snackbag/vera/util/DragHandler.java b/src/main/java/net/snackbag/vera/util/DragHandler.java index c9be1f68..cab5444c 100644 --- a/src/main/java/net/snackbag/vera/util/DragHandler.java +++ b/src/main/java/net/snackbag/vera/util/DragHandler.java @@ -2,7 +2,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VMouseButton; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VMouseDragEvent; import net.snackbag.vera.widget.VWidget; import org.joml.Vector2i; @@ -76,9 +76,9 @@ else return new Vector2i( private static void fireEvents() { switch (button) { - case LEFT -> target.events.fire(Events.Widget.DRAG_LEFT_CLICK, createContext()); - case MIDDLE -> target.events.fire(Events.Widget.DRAG_MIDDLE_CLICK, createContext()); - case RIGHT -> target.events.fire(Events.Widget.DRAG_RIGHT_CLICK, createContext()); + case LEFT -> target.events.fire(VEvents.Widget.DRAG_LEFT_CLICK, createContext()); + case MIDDLE -> target.events.fire(VEvents.Widget.DRAG_MIDDLE_CLICK, createContext()); + case RIGHT -> target.events.fire(VEvents.Widget.DRAG_RIGHT_CLICK, createContext()); } } } diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 6557c610..a3631840 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -4,7 +4,7 @@ import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCheckedStateChange; import net.snackbag.vera.style.StyleState; @@ -44,7 +44,7 @@ public void render() { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); - if (event.equals(Events.Widget.LEFT_CLICK)) setChecked(!checked); + if (event.equals(VEvents.Widget.LEFT_CLICK)) setChecked(!checked); } public boolean isChecked() { @@ -54,10 +54,10 @@ public boolean isChecked() { public void setChecked(boolean checked) { this.checked = checked; - events.fire(Events.CheckBox.CHECK_STATE_CHANGED, checked); + events.fire(VEvents.CheckBox.CHECK_STATE_CHANGED, checked); } public void onCheckStateChange(VCheckedStateChange runnable) { - events.register(Events.CheckBox.CHECK_STATE_CHANGED, args -> runnable.run((boolean) args[0])); + events.register(VEvents.CheckBox.CHECK_STATE_CHANGED, args -> runnable.run((boolean) args[0])); } } diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index e0940611..6caf56b4 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -4,7 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Int; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; @@ -88,8 +88,8 @@ app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight() public void setFocused(boolean focused) { super.setFocused(focused); - if (focused) events.fire(Events.Dropdown.SELECTOR_OPEN); - else events.fire(Events.Dropdown.SELECTOR_CLOSE); + if (focused) events.fire(VEvents.Dropdown.SELECTOR_OPEN); + else events.fire(VEvents.Dropdown.SELECTOR_CLOSE); } public VColor getItemHoverColor() { @@ -140,7 +140,7 @@ public void handleBuiltinEvent(String event, Object... args) { int y = getY(); switch (event) { - case Events.Widget.LEFT_CLICK -> { + case VEvents.Widget.LEFT_CLICK -> { if (isFocused()) { Item target = getHoveredItem(); if (target != null && hoveredItem != null) { @@ -153,7 +153,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case Events.Widget.RIGHT_CLICK -> { + case VEvents.Widget.RIGHT_CLICK -> { if (isFocused()) { Item target = getHoveredItem(); if (target != null && hoveredItem != null) { @@ -166,7 +166,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case Events.Widget.MIDDLE_CLICK -> { + case VEvents.Widget.MIDDLE_CLICK -> { if (isFocused()) { Item target = getHoveredItem(); if (target != null && hoveredItem != null) { @@ -179,7 +179,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case Events.Widget.MOUSE_MOVE -> { + case VEvents.Widget.MOUSE_MOVE -> { if (!isFocused()) hoveredItem = null; else { // Get mouse position relative to the dropdown's top-left corner @@ -194,7 +194,7 @@ public void handleBuiltinEvent(String event, Object... args) { } } - case Events.Widget.HOVER_LEAVE -> hoveredItem = null; + case VEvents.Widget.HOVER_LEAVE -> hoveredItem = null; } super.handleBuiltinEvent(event, args); @@ -209,15 +209,15 @@ private int getItemIndexAt(int mouseY) { } public void onItemSwitch(VItemSwitchEvent runnable) { - events.register(Events.Dropdown.ITEM_SWITCH, args -> runnable.run((int) args[0])); + events.register(VEvents.Dropdown.ITEM_SWITCH, args -> runnable.run((int) args[0])); } public void onSelectorOpen(Runnable runnable) { - events.register(Events.Dropdown.SELECTOR_OPEN, runnable); + events.register(VEvents.Dropdown.SELECTOR_OPEN, runnable); } public void onSelectorClose(Runnable runnable) { - events.register(Events.Dropdown.SELECTOR_CLOSE, runnable); + events.register(VEvents.Dropdown.SELECTOR_CLOSE, runnable); } private @Nullable Item getItemAt(int mouseX, int mouseY) { @@ -250,7 +250,7 @@ public int getSelectedItem() { public void setSelectedItem(int selectedItem) { this.selectedItem = selectedItem; - events.fire(Events.Dropdown.ITEM_SWITCH, selectedItem); + events.fire(VEvents.Dropdown.ITEM_SWITCH, selectedItem); } public void addItem(String name) { diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 76cebb81..5f83b110 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -5,7 +5,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Int; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCharLimitedEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.modifier.VHasPlaceholderFont; @@ -98,7 +98,7 @@ public void handleBuiltinEvent(String event, Object... args) { int x = getX(); - if (event.equals(Events.Widget.LEFT_CLICK)) { + if (event.equals(VEvents.Widget.LEFT_CLICK)) { textSelection.clear(); if (Vera.getMouseX() < x) cursorPos = 0; @@ -117,7 +117,7 @@ public String getText() { public void setText(String text) { this.text = text; - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } public boolean isSelectingText() { @@ -162,23 +162,23 @@ public String getPlaceholderText() { } public void onLineChanged(Runnable runnable) { - events.register(Events.LineInput.CHANGE, runnable); + events.register(VEvents.LineInput.CHANGE, runnable); } public void onCursorMove(Runnable runnable) { - events.register(Events.LineInput.CURSOR_MOVE, runnable); + events.register(VEvents.LineInput.CURSOR_MOVE, runnable); } public void onCursorMoveLeft(Runnable runnable) { - events.register(Events.LineInput.CURSOR_MOVE_LEFT, runnable); + events.register(VEvents.LineInput.CURSOR_MOVE_LEFT, runnable); } public void onCursorMoveRight(Runnable runnable) { - events.register(Events.LineInput.CURSOR_MOVE_RIGHT, runnable); + events.register(VEvents.LineInput.CURSOR_MOVE_RIGHT, runnable); } public void onAddCharLimited(VCharLimitedEvent runnable) { - events.register(Events.LineInput.ADD_CHAR_LIMITED, args -> runnable.run((char) args[0])); + events.register(VEvents.LineInput.ADD_CHAR_LIMITED, args -> runnable.run((char) args[0])); } @Override @@ -263,32 +263,32 @@ else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && cursorPos > 0) { // Handle word navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isAltDown() && cursorPos > 0) { cursorPos = Math.max(0, jumpToWordStart(cursorPos)); - events.fire(Events.LineInput.CURSOR_MOVE); - events.fire(Events.LineInput.CURSOR_MOVE_LEFT); + events.fire(VEvents.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isAltDown() && cursorPos < text.length()) { cursorPos = Math.min(text.length(), jumpToWordEnd(cursorPos)); - events.fire(Events.LineInput.CURSOR_MOVE); - events.fire(Events.LineInput.CURSOR_MOVE_LEFT); + events.fire(VEvents.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } // Handle line navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isCtrlDown()) { cursorPos = 0; - events.fire(Events.LineInput.CURSOR_MOVE); - events.fire(Events.LineInput.CURSOR_MOVE_LEFT); + events.fire(VEvents.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isCtrlDown()) { cursorPos = text.length(); - events.fire(Events.LineInput.CURSOR_MOVE); - events.fire(Events.LineInput.CURSOR_MOVE_RIGHT); + events.fire(VEvents.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE_RIGHT); } // Handle character navigation else if (keyCode == GLFW.GLFW_KEY_LEFT && cursorPos > 0) { cursorPos = Math.max(0, cursorPos - 1); - events.fire(Events.LineInput.CURSOR_MOVE); - events.fire(Events.LineInput.CURSOR_MOVE_LEFT); + events.fire(VEvents.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } else if (keyCode == GLFW.GLFW_KEY_RIGHT && cursorPos < text.length()) { cursorPos = Math.min(text.length(), cursorPos + 1); - events.fire(Events.LineInput.CURSOR_MOVE); - events.fire(Events.LineInput.CURSOR_MOVE_RIGHT); + events.fire(VEvents.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE_RIGHT); } super.keyPressed(keyCode, scanCode, modifiers); @@ -320,12 +320,12 @@ private void handleSelectionKeyPress(int keyCode) { cursorPos = newPos; textSelection.endPos = newPos; - events.fire(Events.LineInput.CURSOR_MOVE); + events.fire(VEvents.LineInput.CURSOR_MOVE); } private void insertText(String insertion) { if (maxChars > -1 && text.length() + insertion.length() > maxChars) { - events.fire(Events.LineInput.ADD_CHAR_LIMITED, insertion.charAt(0)); + events.fire(VEvents.LineInput.ADD_CHAR_LIMITED, insertion.charAt(0)); return; } @@ -333,7 +333,7 @@ private void insertText(String insertion) { String back = text.substring(cursorPos); text = front + insertion + back; cursorPos += insertion.length(); - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } private void deleteSelectedText() { @@ -347,7 +347,7 @@ private void deleteSelectedText() { text = front + back; cursorPos = start; clearTextSelection(); - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } private void replaceSelectedText(String replacement) { @@ -357,7 +357,7 @@ private void replaceSelectedText(String replacement) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + replacement.length() > maxChars) { - events.fire(Events.LineInput.ADD_CHAR_LIMITED, replacement.charAt(0)); + events.fire(VEvents.LineInput.ADD_CHAR_LIMITED, replacement.charAt(0)); return; } @@ -366,7 +366,7 @@ private void replaceSelectedText(String replacement) { text = front + replacement + back; cursorPos = start + replacement.length(); clearTextSelection(); - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } @@ -436,7 +436,7 @@ public void charTyped(char chr, int modifiers) { int end = Math.max(textSelection.startPos, textSelection.endPos); if (maxChars > -1 && text.length() - (end - start) + 1 > maxChars) { - events.fire(Events.LineInput.ADD_CHAR_LIMITED, chr); + events.fire(VEvents.LineInput.ADD_CHAR_LIMITED, chr); return; } @@ -446,11 +446,11 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos = start + 1; clearTextSelection(); - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } else { // Normal character insertion if (maxChars > -1 && text.length() >= maxChars) { - events.fire(Events.LineInput.ADD_CHAR_LIMITED, chr); + events.fire(VEvents.LineInput.ADD_CHAR_LIMITED, chr); return; } @@ -459,7 +459,7 @@ public void charTyped(char chr, int modifiers) { text = front + chr + back; cursorPos += 1; - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } } super.charTyped(chr, modifiers); @@ -538,7 +538,7 @@ private void deleteText(int start, int end) { builder.delete(start, end); text = builder.toString(); cursorPos = Math.min(start, text.length()); - events.fire(Events.LineInput.CHANGE); + events.fire(VEvents.LineInput.CHANGE); } public static class TextSelection { diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 62a78f89..f9aa0314 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -4,7 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.event.Events; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.StyleState; import org.jetbrains.annotations.Nullable; @@ -61,37 +61,37 @@ public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); switch (event) { - case Events.Widget.MOUSE_MOVE -> getHoveredTabIndex((int) args[0]); + case VEvents.Widget.MOUSE_MOVE -> getHoveredTabIndex((int) args[0]); - case Events.Widget.HOVER -> getHoveredTabIndex(Vera.getMouseX()); - case Events.Widget.HOVER_LEAVE -> hoveredTab = null; + case VEvents.Widget.HOVER -> getHoveredTabIndex(Vera.getMouseX()); + case VEvents.Widget.HOVER_LEAVE -> hoveredTab = null; - case Events.Widget.LEFT_CLICK -> { + case VEvents.Widget.LEFT_CLICK -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire(Events.TabWidget.TAB_LEFT_CLICK, hoveredTab); + events.fire(VEvents.TabWidget.TAB_LEFT_CLICK, hoveredTab); setActiveTab(hoveredTab); } - case Events.Widget.LEFT_CLICK_RELEASE -> { + case VEvents.Widget.LEFT_CLICK_RELEASE -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire(Events.TabWidget.TAB_LEFT_CLICK_RELEASE, hoveredTab); + events.fire(VEvents.TabWidget.TAB_LEFT_CLICK_RELEASE, hoveredTab); } - case Events.Widget.MIDDLE_CLICK -> { + case VEvents.Widget.MIDDLE_CLICK -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire(Events.TabWidget.TAB_MIDDLE_CLICK, hoveredTab); + events.fire(VEvents.TabWidget.TAB_MIDDLE_CLICK, hoveredTab); } - case Events.Widget.MIDDLE_CLICK_RELEASE -> { + case VEvents.Widget.MIDDLE_CLICK_RELEASE -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire(Events.TabWidget.TAB_MIDDLE_CLICK_RELEASE, hoveredTab); + events.fire(VEvents.TabWidget.TAB_MIDDLE_CLICK_RELEASE, hoveredTab); } - case Events.Widget.RIGHT_CLICK -> { + case VEvents.Widget.RIGHT_CLICK -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire(Events.TabWidget.TAB_RIGHT_CLICK, hoveredTab); + events.fire(VEvents.TabWidget.TAB_RIGHT_CLICK, hoveredTab); } - case Events.Widget.RIGHT_CLICK_RELEASE -> { + case VEvents.Widget.RIGHT_CLICK_RELEASE -> { if (!isValidTabIndex(hoveredTab)) return; - events.fire(Events.TabWidget.TAB_RIGHT_CLICK_RELEASE, hoveredTab); + events.fire(VEvents.TabWidget.TAB_RIGHT_CLICK_RELEASE, hoveredTab); } } } @@ -122,7 +122,7 @@ public int getHoveredTabIndex(int mouseX) { if (relativeX >= currentX && relativeX < currentX + totalTabWidth) { if (hoveredTab != null && hoveredTab != index) { - events.fire(Events.TabWidget.TAB_HOVER_CHANGE, hoveredTab); + events.fire(VEvents.TabWidget.TAB_HOVER_CHANGE, hoveredTab); } hoveredTab = index; @@ -141,31 +141,31 @@ public int getHoveredTabIndex(int mouseX) { } public void onTabHoverChange(Consumer runnable) { - events.register(Events.TabWidget.TAB_HOVER_CHANGE, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_HOVER_CHANGE, (args) -> runnable.accept((int) args[0])); } public void onTabLeftClick(Consumer runnable) { - events.register(Events.TabWidget.TAB_LEFT_CLICK, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_LEFT_CLICK, (args) -> runnable.accept((int) args[0])); } public void onTabLeftClickRelease(Consumer runnable) { - events.register(Events.TabWidget.TAB_LEFT_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_LEFT_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); } public void onTabMiddleClick(Consumer runnable) { - events.register(Events.TabWidget.TAB_MIDDLE_CLICK, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_MIDDLE_CLICK, (args) -> runnable.accept((int) args[0])); } public void onTabMiddleClickRelease(Consumer runnable) { - events.register(Events.TabWidget.TAB_MIDDLE_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_MIDDLE_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); } public void onTabRightClick(Consumer runnable) { - events.register(Events.TabWidget.TAB_RIGHT_CLICK, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_RIGHT_CLICK, (args) -> runnable.accept((int) args[0])); } public void onTabRightClickRelease(Consumer runnable) { - events.register(Events.TabWidget.TAB_RIGHT_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); + events.register(VEvents.TabWidget.TAB_RIGHT_CLICK_RELEASE, (args) -> runnable.accept((int) args[0])); } public void addTab(String tab, VWidget... widgets) { diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index c8aa7c55..d47e9de7 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -189,109 +189,109 @@ public boolean isHovered() { public void setHovered(boolean hovered) { // If changed if (this.hovered != hovered) { - if (hovered) events.fire(Events.Widget.HOVER); - else events.fire(Events.Widget.HOVER_LEAVE); + if (hovered) events.fire(VEvents.Widget.HOVER); + else events.fire(VEvents.Widget.HOVER_LEAVE); } this.hovered = hovered; } public void onHover(Runnable runnable) { - events.register(Events.Widget.HOVER, runnable); + events.register(VEvents.Widget.HOVER, runnable); } public void onHoverLeave(Runnable runnable) { - events.register(Events.Widget.HOVER_LEAVE, runnable); + events.register(VEvents.Widget.HOVER_LEAVE, runnable); } public void onLeftClick(Runnable runnable) { - events.register(Events.Widget.LEFT_CLICK, runnable); + events.register(VEvents.Widget.LEFT_CLICK, runnable); } public void onLeftClickRelease(Runnable runnable) { - events.register(Events.Widget.LEFT_CLICK_RELEASE, runnable); + events.register(VEvents.Widget.LEFT_CLICK_RELEASE, runnable); } public void onRightClick(Runnable runnable) { - events.register(Events.Widget.RIGHT_CLICK, runnable); + events.register(VEvents.Widget.RIGHT_CLICK, runnable); } public void onRightClickRelease(Runnable runnable) { - events.register(Events.Widget.RIGHT_CLICK_RELEASE, runnable); + events.register(VEvents.Widget.RIGHT_CLICK_RELEASE, runnable); } public void onMiddleClick(Runnable runnable) { - events.register(Events.Widget.MIDDLE_CLICK, runnable); + events.register(VEvents.Widget.MIDDLE_CLICK, runnable); } public void onMiddleClickRelease(Runnable runnable) { - events.register(Events.Widget.MIDDLE_CLICK_RELEASE, runnable); + events.register(VEvents.Widget.MIDDLE_CLICK_RELEASE, runnable); } public void onMouseScroll(VMouseScrollEvent runnable) { - events.register(Events.Widget.SCROLL, args -> runnable.run( + events.register(VEvents.Widget.SCROLL, args -> runnable.run( (int) args[0], (int) args[1], (double) args[2]) ); } public void onMouseMove(VMouseMoveEvent runnable) { - events.register(Events.Widget.MOUSE_MOVE, args -> runnable.run((int) args[0], (int) args[1])); + events.register(VEvents.Widget.MOUSE_MOVE, args -> runnable.run((int) args[0], (int) args[1])); } public void onMouseDragLeft(VMouseDragEvent runnable) { - events.register(Events.Widget.DRAG_LEFT_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register(VEvents.Widget.DRAG_LEFT_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragRight(VMouseDragEvent runnable) { - events.register(Events.Widget.DRAG_RIGHT_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register(VEvents.Widget.DRAG_RIGHT_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onMouseDragMiddle(VMouseDragEvent runnable) { - events.register(Events.Widget.DRAG_MIDDLE_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); + events.register(VEvents.Widget.DRAG_MIDDLE_CLICK, args -> runnable.run((VMouseDragEvent.Context) args[0])); } public void onFocusStateChange(Runnable runnable) { - events.register(Events.Widget.FOCUS_STATE_CHANGE, runnable); + events.register(VEvents.Widget.FOCUS_STATE_CHANGE, runnable); } public void onFilesDropped(VFilesDroppedEvent runnable) { - events.register(Events.Widget.FILES_DROPPED, args -> runnable.run((List) args[0])); + events.register(VEvents.Widget.FILES_DROPPED, args -> runnable.run((List) args[0])); } public void onAnimationBegin(VAnimationBeginEvent runnable) { - events.register(Events.Animation.BEGIN, args -> runnable.run((VAnimation) args[0])); + events.register(VEvents.Animation.BEGIN, args -> runnable.run((VAnimation) args[0])); } public void onAnimationUnwindBegin(VAnimationUnwindEvent runnable) { - events.register(Events.Animation.UNWIND_BEGIN, args -> runnable.run((VAnimation) args[0])); + events.register(VEvents.Animation.UNWIND_BEGIN, args -> runnable.run((VAnimation) args[0])); } public void onAnimationRewindBegin(VAnimationRewindEvent runnable) { - events.register(Events.Animation.REWIND_BEGIN, args -> runnable.run((VAnimation) args[0])); + events.register(VEvents.Animation.REWIND_BEGIN, args -> runnable.run((VAnimation) args[0])); } public void onAnimationFinish(VAnimationFinishEvent runnable) { - events.register(Events.Animation.FINISH, args -> runnable.run((VAnimation) args[0], (long) args[1])); + events.register(VEvents.Animation.FINISH, args -> runnable.run((VAnimation) args[0], (long) args[1])); } @Override public void handleBuiltinEvent(String event, Object... args) { switch (event) { - case Events.Widget.LEFT_CLICK -> { + case VEvents.Widget.LEFT_CLICK -> { if (focusOnClick) { setFocused(true); } leftClickDown = true; } - case Events.Widget.RIGHT_CLICK -> rightClickDown = true; - case Events.Widget.MIDDLE_CLICK -> middleClickDown = true; + case VEvents.Widget.RIGHT_CLICK -> rightClickDown = true; + case VEvents.Widget.MIDDLE_CLICK -> middleClickDown = true; - case Events.Widget.LEFT_CLICK_RELEASE -> clearLeftClickDown(); - case Events.Widget.RIGHT_CLICK_RELEASE -> clearRightClickDown(); - case Events.Widget.MIDDLE_CLICK_RELEASE -> clearMiddleClickDown(); + case VEvents.Widget.LEFT_CLICK_RELEASE -> clearLeftClickDown(); + case VEvents.Widget.RIGHT_CLICK_RELEASE -> clearRightClickDown(); + case VEvents.Widget.MIDDLE_CLICK_RELEASE -> clearMiddleClickDown(); - case Events.Widget.HOVER_LEAVE -> { + case VEvents.Widget.HOVER_LEAVE -> { clearLeftClickDown(); clearRightClickDown(); clearMiddleClickDown(); From d519f8238d9ed8d5a7942dd8e50049bc07066632 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:57:46 +0100 Subject: [PATCH 491/661] Easings -> VEasings --- .../net/snackbag/vera/style/standard/WidgetStandardStyle.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index 36bc6a5a..ebd768ba 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -6,7 +6,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; -import net.snackbag.vera.style.animation.easing.Easings; +import net.snackbag.vera.style.animation.easing.VEasings; import net.snackbag.vera.widget.VWidget; public class WidgetStandardStyle implements VStandardStyle { @@ -21,7 +21,7 @@ public void apply(VStyleSheet sheet) { // Transition sheet.setKey(VWidget.class, "transition", 0); - sheet.setKey(VWidget.class, "transition-easing", Easings.LINEAR); + sheet.setKey(VWidget.class, "transition-easing", VEasings.LINEAR); } @Override From 1b3e3b9ea0b31ff0c556627573448dc93d5254ee Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:57:52 +0100 Subject: [PATCH 492/661] Once -> VOnce --- .../java/net/snackbag/vera/util/{Once.java => VOnce.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/net/snackbag/vera/util/{Once.java => VOnce.java} (95%) diff --git a/src/main/java/net/snackbag/vera/util/Once.java b/src/main/java/net/snackbag/vera/util/VOnce.java similarity index 95% rename from src/main/java/net/snackbag/vera/util/Once.java rename to src/main/java/net/snackbag/vera/util/VOnce.java index 4a6ac64c..e059b551 100644 --- a/src/main/java/net/snackbag/vera/util/Once.java +++ b/src/main/java/net/snackbag/vera/util/VOnce.java @@ -5,7 +5,7 @@ import java.util.Objects; -public class Once { +public class VOnce { @Nullable private T value = null; @@ -59,7 +59,7 @@ public boolean isSet() { @Override public boolean equals(Object o) { - if (!(o instanceof Once once)) return false; + if (!(o instanceof VOnce once)) return false; return Objects.equals(value, once.value); } From 316c5b4331ec53431577320636964e7b9c601316 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:58:23 +0100 Subject: [PATCH 493/661] Geometry -> VGeometry --- src/main/java/net/snackbag/vera/core/VeraApp.java | 6 +++--- .../snackbag/vera/util/{Geometry.java => VGeometry.java} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/net/snackbag/vera/util/{Geometry.java => VGeometry.java} (95%) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 24e4476e..187ee453 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -7,7 +7,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; -import net.snackbag.vera.util.Geometry; +import net.snackbag.vera.util.VGeometry; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -270,13 +270,13 @@ private boolean isPointOverWidget(VWidget widget, int px, int py) { int widgetY = widget.getHitboxY() + y; int widgetWidth = widget.getHitboxWidth(); int widgetHeight = widget.getHitboxHeight(); - return Geometry.isInBox(px, py, widgetX, widgetY, widgetWidth, widgetHeight); + return VGeometry.isInBox(px, py, widgetX, widgetY, widgetWidth, widgetHeight); } public boolean isPointOverThis(int px, int py) { if (!isVisible()) return false; - return Geometry.isInBox(px, py, x, y, width, height); + return VGeometry.isInBox(px, py, x, y, width, height); } public void setFocusedWidget(@Nullable VWidget widget) { diff --git a/src/main/java/net/snackbag/vera/util/Geometry.java b/src/main/java/net/snackbag/vera/util/VGeometry.java similarity index 95% rename from src/main/java/net/snackbag/vera/util/Geometry.java rename to src/main/java/net/snackbag/vera/util/VGeometry.java index 3e66b01c..5d592f21 100644 --- a/src/main/java/net/snackbag/vera/util/Geometry.java +++ b/src/main/java/net/snackbag/vera/util/VGeometry.java @@ -1,6 +1,6 @@ package net.snackbag.vera.util; -public class Geometry { +public class VGeometry { /** * Method to check whether a coordinate in within the boundaries of a box * From 770858c1dfd9002f164932e6c03bf68e4a2142cb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:58:34 +0100 Subject: [PATCH 494/661] Easings -> VEasings --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 94de7127..c71aa270 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -5,7 +5,7 @@ import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Color; import net.snackbag.vera.core.v4.V4Int; -import net.snackbag.vera.style.animation.easing.Easings; +import net.snackbag.vera.style.animation.easing.VEasings; import net.snackbag.vera.style.animation.easing.VEasing; import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; @@ -22,7 +22,7 @@ public enum StyleValueType { .withSize(easing.apply(from.getSize(), to.getSize(), delta)) .withName(delta > 0.5 ? to.getName() : from.getName())), CURSOR(VCursorShape.DEFAULT, (f, t, e, d) -> d > 0.5 ? t : f), - EASING(Easings.LINEAR, (f, t, e, d) -> d > 0.5 ? t : f), + EASING(VEasings.LINEAR, (f, t, e, d) -> d > 0.5 ? t : f), V4INT(new V4Int(0), (from, to, easing, delta) -> new V4Int( easing.apply(from.get1(), to.get1(), delta), @@ -49,7 +49,7 @@ public static StyleValueType get(Object val, @Nullable StyleValueType bias) { if (val instanceof String s) { if (bias == IDENTIFIER && s.matches("^[\\w-./]*:[\\w-./]*$")) return IDENTIFIER; else if (bias == CURSOR && EnumUtils.getEnumIgnoreCase(VCursorShape.class, s) != null) return CURSOR; - else if (bias == EASING && Easings.getIgnoreCase(s) != null) return EASING; + else if (bias == EASING && VEasings.getIgnoreCase(s) != null) return EASING; return STRING; } else if (val instanceof V4Color || (bias == V4COLOR && (val instanceof VColor[] || val instanceof VColor))) return V4COLOR; @@ -69,7 +69,7 @@ public static Object convert(Object value, StyleValueType to) { if (value instanceof String v) { if (to == IDENTIFIER) return new Identifier(v); else if (to == CURSOR) return EnumUtils.getEnumIgnoreCase(VCursorShape.class, v); - else if (to == EASING) return Easings.getIgnoreCase(v); + else if (to == EASING) return VEasings.getIgnoreCase(v); } else if (value instanceof int[] || value instanceof Integer[]) { From 3a81c29e609224b66d7141c965f1c306a20e63f3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:58:50 +0100 Subject: [PATCH 495/661] add StyleValue container to hold StyleValueType and an Object --- src/main/java/net/snackbag/vera/style/StyleValue.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/StyleValue.java diff --git a/src/main/java/net/snackbag/vera/style/StyleValue.java b/src/main/java/net/snackbag/vera/style/StyleValue.java new file mode 100644 index 00000000..e39e3ef4 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/StyleValue.java @@ -0,0 +1,4 @@ +package net.snackbag.vera.style; + +public record StyleValue(StyleValueType type, Object value) { +} From d95f798f99f6a6a8bcebeb443bee50b9bf3f1259 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 14:59:54 +0100 Subject: [PATCH 496/661] new basic animation test --- .../mcvera/test/StyleTestApplication.java | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 4536b43b..e9515493 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -1,35 +1,22 @@ package net.snackbag.mcvera.test; import net.snackbag.vera.core.VColor; -import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.VStyleSheet; -import net.snackbag.vera.style.animation.LoopMode; import net.snackbag.vera.style.animation.VAnimation; -import net.snackbag.vera.style.animation.easing.Easings; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; public class StyleTestApplication extends VeraApp { public static StyleTestApplication INSTANCE = new StyleTestApplication(); - private final VAnimation testAnimation = new VAnimation.Builder(this, "test") - .unwindTime(2000) - .unwindOnFinish() - .loop(LoopMode.REPEAT) - - .keyframe(1000, frame -> frame.style("background-color", VColor.MC_GOLD), 2000) - .keyframe(1000, frame -> frame.style("background-color", VColor.MC_RED), 2000) - .build(); - - private final VAnimation hoverAnimation = new VAnimation.Builder(this, "hover") - .unwindTime(1000) - .keepFinalStyle(StyleState.HOVERED) - - .keyframe(1000, frame -> frame.style("background-color", VColor.MC_WHITE), 0) + private final VAnimation testAnimation = new VAnimation.Builder("test") + .keyframe(1000, 5000, frame -> { + frame.style("background-color", VColor.MC_RED); + }) .build(); @Override @@ -43,19 +30,7 @@ public void init() { .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); -// testRect.onHover(() -> testRect.animations.activateOrRewind(hoverAnimation)); -// testRect.onHoverLeave(() -> testRect.animations.unwind(hoverAnimation)); - - testRect.setStyle("cursor", StyleState.HOVERED, VCursorShape.POINTING_HAND); - testRect.setStyle("cursor", StyleState.CLICKED, VCursorShape.ALL_RESIZE); - testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); - - testRect.onMouseDragLeft((ctx) -> testRect.move(testRect.getX() + ctx.moveX(), testRect.getY() + ctx.moveY())); - - new VShortcut(this, "a", () -> testRect.animations.activate(testAnimation)); - new VShortcut(this, "u", () -> testRect.animations.unwind(testAnimation)); - new VShortcut(this, "r", () -> testRect.animations.rewind(testAnimation)); - new VShortcut(this, "k", () -> testRect.animations.kill(testAnimation)); + testRect.onLeftClick(() -> testRect.animate(testAnimation)); } public VStyleSheet createStyleSheet() { From d326e00f80e6c6b4991482a50162baa43d246ea6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 18:21:45 +0100 Subject: [PATCH 497/661] very basic animation system --- .../style/animation/CompiledAnimation.java | 55 +++++++++ .../vera/style/animation/VAnimation.java | 99 +++++++++++++++++ .../vera/style/animation/VKeyframe.java | 104 ++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/VAnimation.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/VKeyframe.java diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java new file mode 100644 index 00000000..d7c6d45f --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -0,0 +1,55 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.widget.VWidget; + +import java.util.List; + +/** + * Version of the animation class that is immutable and has additional values: {@link #app}, {@link #duration} and + * {@link #keys}. The {@link #keyframes} variable has also been altered drastically. + */ +public class CompiledAnimation { + /** + * Assigned app, used for stylesheets + */ + public final VeraApp app; + + public final String name; + /** + * Sum of all keyframes's transition time + stay time + */ + public final int duration; + + /** + * List of all keyframes. The difference to the normal {@link VAnimation} object is that each keyframe has the + * style keys of each keyframe in the entire animation, even if unchanged. + */ + public final List keyframes; + /** + * All keys that are affected by the animation. Used in caching to calculate animation styles. + */ + public final List keys; + + /** + * Does not compile anything by itself, it simply accepts precompiled values. Compilation occurs in + * {@link VAnimation#compile(VeraApp, VWidget)} + */ + protected CompiledAnimation( + VeraApp app, + String name, int duration, + List keyframes, List keys + ) { + this.app = app; + + this.name = name; + this.duration = duration; + this.keyframes = keyframes; + this.keys = keys; + } + + @Override + public String toString() { + return "CompiledAnimation{name='%s', duration='%s', keyframeCount='%s'}".formatted(name, duration, keyframes.size()); + } +} diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java new file mode 100644 index 00000000..7da3eb81 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -0,0 +1,99 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleValue; +import net.snackbag.vera.style.animation.easing.VEasings; +import net.snackbag.vera.style.animation.easing.VEasing; +import net.snackbag.vera.widget.VWidget; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; + +public class VAnimation { + public String name; + public List keyframes; + + public VAnimation(String name, List keyframes) { + this.name = name; + this.keyframes = keyframes; + } + + public CompiledAnimation compile(VeraApp app, VWidget widget) { + VKeyframe.ListIndex index = VKeyframe.createIndex(keyframes, app.styleSheet); + + List extendedFrames = new ArrayList<>(); + HashMap styleMemory = new HashMap<>(); + + for (VKeyframe original : keyframes) { + VKeyframe frame = new VKeyframe(original); // copy keyframe + HashMap frameStyles = index.keyframeValueMap.get(original); // get all registered styles + + // fill all styles & memorize + for (String style : index.styles) { + if (frame.styles.containsKey(style)) { + // skip & remove keys that weren't reserved when the index was created; fixes NPE + if (frameStyles.get(style) == null || app.styleSheet.getKey(widget, style) == null) { + frame.styles.remove(style); + continue; + } + + styleMemory.put(style, frameStyles.get(style)); + continue; + } + + // set default style + if (!styleMemory.containsKey(style)) { + styleMemory.put(style, new StyleValue( + app.styleSheet.getReservation(style), + app.styleSheet.getKey(widget, style)) + ); + } + frame.style(style, styleMemory.get(style).value()); // actual filling + } + + extendedFrames.add(frame); + } + + return new CompiledAnimation( + app, + name, index.totalDuration, + extendedFrames, index.styles + ); + } + + @Override + public String toString() { + return "VAnimation{name='%s'}".formatted(name); + } + + public static class Builder { + private final String name; + private final List keyframes = new ArrayList<>(); + + public Builder(String name) { + this.name = name; + } + + public Builder keyframe(int stayMs, Consumer apply) { + return keyframe(0, stayMs, apply, VEasings.LINEAR); + } + + public Builder keyframe(int transitionMs, int stayMs, Consumer apply) { + return keyframe(transitionMs, stayMs, apply, VEasings.LINEAR); + } + + public Builder keyframe(int transitionMs, int stayMs, Consumer apply, VEasing easing) { + VKeyframe frame = new VKeyframe(stayMs, transitionMs, easing); + apply.accept(frame); + + keyframes.add(frame); + return this; + } + + public VAnimation build() { + return new VAnimation(name, keyframes); + } + } +} diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java new file mode 100644 index 00000000..bc7fa42a --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -0,0 +1,104 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.vera.style.StyleValue; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.VStyleSheet; +import net.snackbag.vera.style.animation.easing.VEasing; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class VKeyframe { + protected final int stayTime; + protected final int transitionTime; + protected final VEasing easing; + + protected final HashMap styles = new HashMap<>(); + + public VKeyframe(int stayTime, int transitionTime, VEasing easing) { + this.stayTime = stayTime; + this.transitionTime = transitionTime; + this.easing = easing; + } + + public VKeyframe(VKeyframe other) { + this.stayTime = other.stayTime; + this.transitionTime = other.transitionTime; + this.easing = other.easing; + + styles.putAll(other.styles); + } + + public void style(String key, Object value) { + if (styles.containsKey(key)) throw new RuntimeException("Key '%s' cannot be reassigned within the same keyframe".formatted(key)); + styles.put(key, value); + } + + public String dump() { + StringBuilder sb = new StringBuilder(); + sb.append("stayTime=").append(stayTime).append('\n'); + sb.append("transitionTime=").append(transitionTime).append('\n'); + sb.append("easing=").append(easing).append('\n'); + for (var e : styles.entrySet()) { + sb.append(e.getKey()).append(" = ").append(e.getValue()).append('\n'); + } + return sb.toString(); + } + + public static class ListIndex { + public final List styles = new ArrayList<>(); + public final LinkedHashMap> keyframeValueMap = new LinkedHashMap<>(); + public final int totalDuration; + + /** + * Gathers styles and total duration of a given list of keyframes. + * + * @param keyframes the list of keyframes + * @param sheet the stylesheet to check keyframe style registration. If null, check will be omitted + */ + private ListIndex(List keyframes, @Nullable VStyleSheet sheet) { + int dur = 0; + + // counting + for (VKeyframe frame : keyframes) { + HashMap valueMap = new HashMap<>(); + dur += frame.transitionTime + frame.stayTime; + + for (Map.Entry entry : frame.styles.entrySet()) { + String key = entry.getKey(); + Object val = entry.getValue(); + + if (styles.contains(key)) continue; + styles.add(key); + + SheetCheck: if (sheet != null) { + StyleValueType res = sheet.getReservation(key); + if (res == null) { + MinecraftVera.LOGGER.warn("Cannot set keyframe style to unreserved style key: %s. Key removed.".formatted(key)); + styles.remove(key); + frame.styles.remove(key); + break SheetCheck; + } + + StyleValueType testRes = StyleValueType.get(val, res); + if (testRes != res) throw new RuntimeException( + "Cannot create keyframe with key '%s', because it has an incorrect value type. Got: %s, require %s" + .formatted(key, testRes, res)); + + valueMap.put(key, new StyleValue(res, val)); + } + + keyframeValueMap.put(frame, valueMap); + } + } + + // apply values + totalDuration = dur; + } + } + + public static ListIndex createIndex(List keyframes, @Nullable VStyleSheet sheet) { + return new ListIndex(keyframes, sheet); + } +} From 9500182273f578159cf80f8814ebc079ad1a6364 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 18:22:08 +0100 Subject: [PATCH 498/661] mark getKey methods as nullable --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 99147da5..11bb778a 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -16,11 +16,11 @@ public class VStyleSheet { private HashMap typeRegistry = new HashMap<>(); - public T getKey(VWidget widget, String key) { + public @Nullable T getKey(VWidget widget, String key) { return getKey(widget, key, StyleState.DEFAULT); } - public T getKey(VWidget widget, String key, @Nullable StyleState state) { + public @Nullable T getKey(VWidget widget, String key, @Nullable StyleState state) { if (state == null) state = StyleState.DEFAULT; // if widget contains key From 7e29f3511eb5e80b3350b9a4a228bd80029beb19 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 18:39:55 +0100 Subject: [PATCH 499/661] add IMMEDIATE easing --- .../vera/style/animation/easing/VEasings.java | 1 + .../animation/easing/VImmediateEasing.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index ba9e5f2e..f473a696 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -3,6 +3,7 @@ import net.snackbag.vera.Vera; public class VEasings { + public static final VImmediateEasing IMMEDIATE = new VImmediateEasing(); public static final VLinearEasing LINEAR = new VLinearEasing(); public static VEasing getIgnoreCase(String name) { diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java new file mode 100644 index 00000000..3260443f --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java @@ -0,0 +1,17 @@ +package net.snackbag.vera.style.animation.easing; + +public class VImmediateEasing extends VEasing { + public VImmediateEasing() { + super("immediate"); + } + + @Override + public float apply(float from, float to, float delta) { + return to; + } + + @Override + public int apply(int from, int to, float delta) { + return to; + } +} From 19f7635c1c9b47e6b7633d7127bc3459485a7df5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 18:40:15 +0100 Subject: [PATCH 500/661] immediate 0 0 keyframe --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 7da3eb81..7bf01e85 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -24,9 +24,12 @@ public CompiledAnimation compile(VeraApp app, VWidget widget) { VKeyframe.ListIndex index = VKeyframe.createIndex(keyframes, app.styleSheet); List extendedFrames = new ArrayList<>(); + List loopingFrames = new ArrayList<>(keyframes); HashMap styleMemory = new HashMap<>(); - for (VKeyframe original : keyframes) { + loopingFrames.add(0, new VKeyframe(0, 0, VEasings.IMMEDIATE)); + + for (VKeyframe original : loopingFrames) { VKeyframe frame = new VKeyframe(original); // copy keyframe HashMap frameStyles = index.keyframeValueMap.get(original); // get all registered styles From 718fd9fc63bbca93a5e1e890825aa2c1c4b47739 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 18:40:28 +0100 Subject: [PATCH 501/661] filling -> populating --- .../java/net/snackbag/vera/style/animation/VAnimation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 7bf01e85..180db81f 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -33,7 +33,7 @@ public CompiledAnimation compile(VeraApp app, VWidget widget) { VKeyframe frame = new VKeyframe(original); // copy keyframe HashMap frameStyles = index.keyframeValueMap.get(original); // get all registered styles - // fill all styles & memorize + // populate all styles & memorize for (String style : index.styles) { if (frame.styles.containsKey(style)) { // skip & remove keys that weren't reserved when the index was created; fixes NPE @@ -53,7 +53,7 @@ public CompiledAnimation compile(VeraApp app, VWidget widget) { app.styleSheet.getKey(widget, style)) ); } - frame.style(style, styleMemory.get(style).value()); // actual filling + frame.style(style, styleMemory.get(style).value()); // actual populating } extendedFrames.add(frame); From 0db1283fe5efb7024fdc861d883f35b483ff3feb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 15 Feb 2026 20:56:15 +0100 Subject: [PATCH 502/661] add unwindTime and unwindEasing --- .../style/animation/CompiledAnimation.java | 11 +++++++-- .../vera/style/animation/VAnimation.java | 23 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index d7c6d45f..1d08c417 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -1,6 +1,7 @@ package net.snackbag.vera.style.animation; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.widget.VWidget; import java.util.List; @@ -16,11 +17,13 @@ public class CompiledAnimation { public final VeraApp app; public final String name; + public final VEasing unwindEasing; + public final int unwindTime; + /** * Sum of all keyframes's transition time + stay time */ public final int duration; - /** * List of all keyframes. The difference to the normal {@link VAnimation} object is that each keyframe has the * style keys of each keyframe in the entire animation, even if unchanged. @@ -38,18 +41,22 @@ public class CompiledAnimation { protected CompiledAnimation( VeraApp app, String name, int duration, + VEasing unwindEasing, int unwindTime, List keyframes, List keys ) { this.app = app; this.name = name; this.duration = duration; + this.unwindEasing = unwindEasing; + this.unwindTime = unwindTime; this.keyframes = keyframes; this.keys = keys; } @Override public String toString() { - return "CompiledAnimation{name='%s', duration='%s', keyframeCount='%s'}".formatted(name, duration, keyframes.size()); + return "CompiledAnimation{name='%s', duration='%s', unwindEasing='%s', unwindTime=%s, keyframeCount=%s}" + .formatted(name, duration, unwindEasing, unwindTime, keyframes.size()); } } diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 180db81f..754d00f1 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -15,9 +15,14 @@ public class VAnimation { public String name; public List keyframes; - public VAnimation(String name, List keyframes) { + public VEasing unwindEasing; + public int unwindTime; + + public VAnimation(String name, VEasing unwindEasing, int unwindTime, List keyframes) { this.name = name; this.keyframes = keyframes; + this.unwindEasing = unwindEasing; + this.unwindTime = unwindTime; } public CompiledAnimation compile(VeraApp app, VWidget widget) { @@ -62,6 +67,7 @@ public CompiledAnimation compile(VeraApp app, VWidget widget) { return new CompiledAnimation( app, name, index.totalDuration, + unwindEasing, unwindTime, extendedFrames, index.styles ); } @@ -75,6 +81,9 @@ public static class Builder { private final String name; private final List keyframes = new ArrayList<>(); + private VEasing unwindEasing = VEasings.LINEAR; + private int unwindTime = 0; + public Builder(String name) { this.name = name; } @@ -95,8 +104,18 @@ public Builder keyframe(int transitionMs, int stayMs, Consumer apply, return this; } + public Builder unwindEasing(VEasing easing) { + this.unwindEasing = easing; + return this; + } + + public Builder unwindTime(int ms) { + this.unwindTime = ms; + return this; + } + public VAnimation build() { - return new VAnimation(name, keyframes); + return new VAnimation(name, unwindEasing, unwindTime, keyframes); } } } From 3e68baca331adc993d826dbec0de97d613b29187 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 13:56:23 +0100 Subject: [PATCH 503/661] implement per-widget animation engine --- .../vera/style/animation/AnimationEngine.java | 65 +++++++++++++++++++ .../net/snackbag/vera/widget/VWidget.java | 5 ++ 2 files changed, 70 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java new file mode 100644 index 00000000..72f55703 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -0,0 +1,65 @@ +package net.snackbag.vera.style.animation; + +import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.widget.VWidget; + +import java.util.HashMap; +import java.util.Map; + +public class AnimationEngine { + public final VWidget widget; + private final HashMap active = new HashMap<>(); + + public AnimationEngine(VWidget widget) { + this.widget = widget; + } + + public void start(VAnimation animation) { + if (active.containsKey(animation.name)) { + MinecraftVera.LOGGER.warn("Couldn't start animation %s, because it's already running".formatted(animation.name)); + return; + } + + CompiledAnimation compiled = animation.compile(widget.app, widget); + active.put(compiled.name, new PlaybackContext(compiled, System.currentTimeMillis())); + } + + public void stop(VAnimation animation) { + stop(animation.name); + } + + public void stop(String name) { + if (!active.containsKey(name)) { + MinecraftVera.LOGGER.warn("Couldn't stop animation %s, because it isn't active".formatted(name)); + return; + } + + active.remove(name); + } + + public T animateStyle(String key, T value) { + for (Map.Entry entry : active.entrySet()) { + PlaybackContext ctx = entry.getValue(); + CompiledAnimation animation = ctx.animation; + if (!animation.keys.contains(key)) continue; + + int time = ctx.getRelativeTime(); + + // select active keyframe + int kfIndex = animation.getKeyframeIndexAtTime(time); + float delta = animation.getKeyframeDelta(time); + // TODO: reverse order because I did a stupid + VKeyframe from = animation.keyframes.get(kfIndex); + VKeyframe to = animation.keyframes.size() - 1 >= kfIndex ? from : animation.keyframes.get(kfIndex + 1); + System.out.println(time + " " + kfIndex); + + StyleValueType reservation = widget.app.styleSheet.getReservation(key); + return (T) reservation.animationTransition.apply( + from.styles.get(key), to.styles.get(key), + from.easing, delta); + } + + return value; + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index d47e9de7..80f3f083 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -8,12 +8,15 @@ import net.snackbag.vera.event.*; import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.util.DragHandler; import java.nio.file.Path; import java.util.*; public abstract class VWidget> extends VElement { + public AnimationEngine animations = new AnimationEngine(this); protected double rotation; public boolean focusOnClick = true; @@ -62,10 +65,12 @@ public void setStyle(String key, StyleState state, V... value) { } public V getStyle(String key) { +// return app.styleSheet.getKey(this, key); return animations.animateStyle(key, app.styleSheet.getKey(this, key)); } public V getStyle(String key, StyleState state) { +// return app.styleSheet.getKey(this, key, state); return animations.animateStyle(key, app.styleSheet.getKey(this, key, state)); } From a7f0bbea80e68cb35fe72998f95ba6fece2c496b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 13:56:28 +0100 Subject: [PATCH 504/661] fix NPE --- src/main/java/net/snackbag/vera/style/animation/VKeyframe.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index bc7fa42a..ca071e18 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -69,8 +69,7 @@ private ListIndex(List keyframes, @Nullable VStyleSheet sheet) { String key = entry.getKey(); Object val = entry.getValue(); - if (styles.contains(key)) continue; - styles.add(key); + if (!styles.contains(key)) styles.add(key); SheetCheck: if (sheet != null) { StyleValueType res = sheet.getReservation(key); From 3bd2d17eed9f7f505d53fc63e382a3a657541fe0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 13:56:37 +0100 Subject: [PATCH 505/661] make animation names case insensitive --- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 754d00f1..671fb292 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -19,7 +19,7 @@ public class VAnimation { public int unwindTime; public VAnimation(String name, VEasing unwindEasing, int unwindTime, List keyframes) { - this.name = name; + this.name = name.toLowerCase(); this.keyframes = keyframes; this.unwindEasing = unwindEasing; this.unwindTime = unwindTime; From 98e9ee6716708f14ecdffa93eb14d1ac63ce5f66 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 13:56:48 +0100 Subject: [PATCH 506/661] add longer test animation --- .../mcvera/test/StyleTestApplication.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index e9515493..6fdfcbd2 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -18,6 +18,17 @@ public class StyleTestApplication extends VeraApp { frame.style("background-color", VColor.MC_RED); }) .build(); + private final VAnimation longTestAnimation = new VAnimation.Builder("long_test") + .keyframe(1000, 2000, frame -> { + frame.style("background-color", VColor.MC_RED); + }) + .keyframe(1000, 5000, frame -> { + frame.style("background-color", VColor.MC_GOLD); + }) + .keyframe(1000, 1000, frame -> { + frame.style("background-color", VColor.MC_WHITE); + }) + .build(); @Override public void init() { @@ -30,7 +41,17 @@ public void init() { .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); - testRect.onLeftClick(() -> testRect.animate(testAnimation)); + testRect.onLeftClick(() -> { +// CompiledAnimation compiled = testAnimation.compile(this, testRect); +// System.out.println(compiled); +// CompiledAnimation compiled = longTestAnimation.compile(this, testRect); +// System.out.println(compiled); + testRect.animations.start(longTestAnimation); + }); + + testRect.onRightClick(() -> { + testRect.animations.stop(longTestAnimation); + }); } public VStyleSheet createStyleSheet() { From 5e3c2a6bccab3d9f682e5fce636fb6550c6fbf77 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 13:56:56 +0100 Subject: [PATCH 507/661] very basic playback context --- .../vera/style/animation/PlaybackContext.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java new file mode 100644 index 00000000..69b4bb9a --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -0,0 +1,20 @@ +package net.snackbag.vera.style.animation; + +// TODO: winding +public class PlaybackContext { + public final CompiledAnimation animation; + public final long startTime; + + public PlaybackContext(CompiledAnimation animation, long startTime) { + this.animation = animation; + this.startTime = startTime; + } + + public int getRelativeTime() { + return Math.toIntExact(System.currentTimeMillis() - startTime) % animation.duration; + } + + public float getProgress() { + return (float) getRelativeTime() / animation.duration; + } +} From 65445f0022cc9582f8735cd41ca7d441a6577f89 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:04:01 +0100 Subject: [PATCH 508/661] add getKeyframeIndexAtTime --- .../vera/style/animation/CompiledAnimation.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index 1d08c417..7a6806d4 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -59,4 +59,20 @@ public String toString() { return "CompiledAnimation{name='%s', duration='%s', unwindEasing='%s', unwindTime=%s, keyframeCount=%s}" .formatted(name, duration, unwindEasing, unwindTime, keyframes.size()); } + + public int getKeyframeIndexAtTime(int time) { + int bufferTime = 0; + + for (int i = 0; i < keyframes.size(); i++) { + VKeyframe keyframe = keyframes.get(i); + + if (time > bufferTime && time < bufferTime + keyframe.transitionTime + keyframe.stayTime) { + return i; + } + + bufferTime += keyframe.transitionTime + keyframe.stayTime; + } + + return keyframes.size() - 1; + } } From 9969c8888a13925f2752bd9e7487e881e085466c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:05:53 +0100 Subject: [PATCH 509/661] change stay -> transition logic to transition -> stay --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 72f55703..986173a6 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -49,10 +49,9 @@ public T animateStyle(String key, T value) { // select active keyframe int kfIndex = animation.getKeyframeIndexAtTime(time); float delta = animation.getKeyframeDelta(time); - // TODO: reverse order because I did a stupid - VKeyframe from = animation.keyframes.get(kfIndex); - VKeyframe to = animation.keyframes.size() - 1 >= kfIndex ? from : animation.keyframes.get(kfIndex + 1); - System.out.println(time + " " + kfIndex); + + VKeyframe to = animation.keyframes.get(kfIndex); + VKeyframe from = animation.keyframes.get(Math.max(kfIndex - 1, 0)); StyleValueType reservation = widget.app.styleSheet.getReservation(key); return (T) reservation.animationTransition.apply( From f02df6c48b320437b26201bc52d4afdfed99b15d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:06:03 +0100 Subject: [PATCH 510/661] remove redundant code --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 6fdfcbd2..392aa550 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -42,10 +42,6 @@ public void init() { VRect testRect = new VRect(VColor.black(), this).alsoAdd(); testRect.onLeftClick(() -> { -// CompiledAnimation compiled = testAnimation.compile(this, testRect); -// System.out.println(compiled); -// CompiledAnimation compiled = longTestAnimation.compile(this, testRect); -// System.out.println(compiled); testRect.animations.start(longTestAnimation); }); From 76639b46fa7212ad9a8577910cc3edf4cfd71582 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:39:07 +0100 Subject: [PATCH 511/661] fix delta --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 986173a6..acbf4ce3 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -48,15 +48,16 @@ public T animateStyle(String key, T value) { // select active keyframe int kfIndex = animation.getKeyframeIndexAtTime(time); - float delta = animation.getKeyframeDelta(time); VKeyframe to = animation.keyframes.get(kfIndex); VKeyframe from = animation.keyframes.get(Math.max(kfIndex - 1, 0)); + float delta = animation.getKeyframeDelta(time, from, to); + StyleValueType reservation = widget.app.styleSheet.getReservation(key); return (T) reservation.animationTransition.apply( from.styles.get(key), to.styles.get(key), - from.easing, delta); + to.easing, delta); } return value; From 2671166e3ce7c8caaaf639b9c04529a73215b2fe Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:39:15 +0100 Subject: [PATCH 512/661] skip immediate keyframes --- .../net/snackbag/vera/style/animation/CompiledAnimation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index 7a6806d4..cb94930d 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -66,6 +66,7 @@ public int getKeyframeIndexAtTime(int time) { for (int i = 0; i < keyframes.size(); i++) { VKeyframe keyframe = keyframes.get(i); + if (keyframe.transitionTime <= 0 && keyframe.stayTime <= 0) continue; if (time > bufferTime && time < bufferTime + keyframe.transitionTime + keyframe.stayTime) { return i; } From e2848a9adb08c56e05fab8c8b6197d0e2549ccff Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:39:23 +0100 Subject: [PATCH 513/661] calculate delta! --- .../style/animation/CompiledAnimation.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index cb94930d..bc17766b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -76,4 +76,26 @@ public int getKeyframeIndexAtTime(int time) { return keyframes.size() - 1; } + + public int getKeyframeTimeAt(VKeyframe keyframe) { + int buffer = 0; + + for (VKeyframe frame : keyframes) { + if (frame == keyframe) break; + buffer += frame.transitionTime + frame.stayTime; + } + + return buffer; + } + + public float getKeyframeDelta(int time, VKeyframe from, VKeyframe to) { + int fromTime = getKeyframeTimeAt(from) + from.transitionTime + from.stayTime; + int toTime = fromTime + to.transitionTime; + + if (time > toTime) return 1f; + else if (time < toTime && time > fromTime) return (float) (time - fromTime) / (toTime - fromTime); + + // time < fromTime + return 0f; + } } From ff00daad6806b7b8182f24cd58a63a994526d616 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:40:25 +0100 Subject: [PATCH 514/661] fix fast single-frame flashes --- .../net/snackbag/vera/style/animation/CompiledAnimation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index bc17766b..1ea7c301 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -93,7 +93,7 @@ public float getKeyframeDelta(int time, VKeyframe from, VKeyframe to) { int toTime = fromTime + to.transitionTime; if (time > toTime) return 1f; - else if (time < toTime && time > fromTime) return (float) (time - fromTime) / (toTime - fromTime); + else if (time > fromTime) return (float) (time - fromTime) / (toTime - fromTime); // time <= toTime already true // time < fromTime return 0f; From 46a6688b7a6c3d4f11d40154149e5e28a5b1156b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:46:30 +0100 Subject: [PATCH 515/661] work via entries --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index acbf4ce3..246edc56 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -39,8 +39,7 @@ public void stop(String name) { } public T animateStyle(String key, T value) { - for (Map.Entry entry : active.entrySet()) { - PlaybackContext ctx = entry.getValue(); + for (PlaybackContext ctx : active.values()) { CompiledAnimation animation = ctx.animation; if (!animation.keys.contains(key)) continue; From e256f7a85b5b9a6209e0c36d66d3d04a5019fe67 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:46:39 +0100 Subject: [PATCH 516/661] mark TODO for loop modes --- .../java/net/snackbag/vera/style/animation/PlaybackContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index 69b4bb9a..eca11e52 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -11,7 +11,7 @@ public PlaybackContext(CompiledAnimation animation, long startTime) { } public int getRelativeTime() { - return Math.toIntExact(System.currentTimeMillis() - startTime) % animation.duration; + return Math.toIntExact(System.currentTimeMillis() - startTime) % animation.duration; // TODO: loop modes } public float getProgress() { From 6a712ed734d78624cae393466c1d3e3e79330f5d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 14:46:51 +0100 Subject: [PATCH 517/661] limit getProgress() to 1.0f --- .../java/net/snackbag/vera/style/animation/PlaybackContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index eca11e52..95b893d0 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -15,6 +15,6 @@ public int getRelativeTime() { } public float getProgress() { - return (float) getRelativeTime() / animation.duration; + return Math.min((float) getRelativeTime() / animation.duration, 1.0f); } } From bbe20f7631823a1e97a758d02763aa01f91e1d7a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 16:23:28 +0100 Subject: [PATCH 518/661] add copyable way for keyframes with mutable transition and stay time --- .../net/snackbag/vera/style/animation/VKeyframe.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java index ca071e18..25e0468c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java +++ b/src/main/java/net/snackbag/vera/style/animation/VKeyframe.java @@ -23,9 +23,13 @@ public VKeyframe(int stayTime, int transitionTime, VEasing easing) { } public VKeyframe(VKeyframe other) { - this.stayTime = other.stayTime; - this.transitionTime = other.transitionTime; - this.easing = other.easing; + this(other, other.transitionTime, other.stayTime, other.easing); + } + + public VKeyframe(VKeyframe other, int transitionTime, int stayTime, VEasing easing) { + this.transitionTime = transitionTime; + this.stayTime = stayTime; + this.easing = easing; styles.putAll(other.styles); } @@ -37,8 +41,8 @@ public void style(String key, Object value) { public String dump() { StringBuilder sb = new StringBuilder(); - sb.append("stayTime=").append(stayTime).append('\n'); sb.append("transitionTime=").append(transitionTime).append('\n'); + sb.append("stayTime=").append(stayTime).append('\n'); sb.append("easing=").append(easing).append('\n'); for (var e : styles.entrySet()) { sb.append(e.getKey()).append(" = ").append(e.getValue()).append('\n'); From a05d842fcb3f0a1ba32909092f1bd2e3438fdeb9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:12:33 +0100 Subject: [PATCH 519/661] add getCurrentLoopNumber --- .../net/snackbag/vera/style/animation/PlaybackContext.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index 95b893d0..a7efa331 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -17,4 +17,8 @@ public int getRelativeTime() { public float getProgress() { return Math.min((float) getRelativeTime() / animation.duration, 1.0f); } + + public int getCurrentLoopNumber() { + return (int) Math.ceil((float) (System.currentTimeMillis() - startTime) / animation.duration); + } } From 880247dbef8d2c818246df069f9094b7e2b87f9a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:12:58 +0100 Subject: [PATCH 520/661] handle lifetimes --- .../net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- .../vera/style/animation/AnimationEngine.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 984093e8..fff8ce44 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -75,7 +75,7 @@ public void renderApp(VeraApp app) { else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); widget.beforeRender(); - widget.animations.update(); + widget.animations.updateLifetimes(); if (widget.visibilityConditionsPassed()) { widget.render(); diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 246edc56..1a0363e1 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -61,4 +61,16 @@ public T animateStyle(String key, T value) { return value; } + + public void updateLifetimes() { + for (Map.Entry entry : active.entrySet()) { + PlaybackContext ctx = entry.getValue(); + CompiledAnimation animation = ctx.animation; + + if (animation.loopMode == VLoopMode.NONE && ctx.getProgress() >= 1.0f) { + // TODO: unwinding + stop(entry.getKey()); + } + } + } } From bf29be590456cbf74fe63a7298c8998cd2de8e93 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:13:24 +0100 Subject: [PATCH 521/661] implement loop modes --- .../mcvera/test/StyleTestApplication.java | 2 ++ .../vera/style/animation/AnimationEngine.java | 22 ++++++++++++++---- .../style/animation/CompiledAnimation.java | 11 +++++++-- .../vera/style/animation/PlaybackContext.java | 7 +++++- .../vera/style/animation/VAnimation.java | 23 +++++++++++++++++-- .../vera/style/animation/VLoopMode.java | 8 +++++++ 6 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/animation/VLoopMode.java diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 392aa550..2c5754e1 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -7,6 +7,7 @@ import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.style.animation.VAnimation; +import net.snackbag.vera.style.animation.VLoopMode; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -19,6 +20,7 @@ public class StyleTestApplication extends VeraApp { }) .build(); private final VAnimation longTestAnimation = new VAnimation.Builder("long_test") + .loopMode(VLoopMode.FORWARD_REPEAT) .keyframe(1000, 2000, frame -> { frame.style("background-color", VColor.MC_RED); }) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 1a0363e1..8af84a87 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -45,16 +45,30 @@ public T animateStyle(String key, T value) { int time = ctx.getRelativeTime(); - // select active keyframe + // select active keyframes int kfIndex = animation.getKeyframeIndexAtTime(time); + int fromKfIndex = Math.max(kfIndex - 1, 0); + boolean loopSpoofed = false; + + if (ctx.getCurrentLoopNumber() > 1) { // handle loop mode; spoof first keyframe with last one for smooth transition + if (fromKfIndex == 0) { + fromKfIndex = animation.keyframes.size() - 1; + loopSpoofed = true; + } + } VKeyframe to = animation.keyframes.get(kfIndex); - VKeyframe from = animation.keyframes.get(Math.max(kfIndex - 1, 0)); + VKeyframe from = animation.keyframes.get(fromKfIndex); + + int fromKfWhen = animation.getWhenKeyframe(from); + if (ctx.getCurrentLoopNumber() > 1 && loopSpoofed) { // handle loop mode; spoof beginning time for smooth transition + fromKfWhen = 0; + } - float delta = animation.getKeyframeDelta(time, from, to); + float delta = animation.getKeyframeDelta(time, fromKfWhen, from, to); StyleValueType reservation = widget.app.styleSheet.getReservation(key); - return (T) reservation.animationTransition.apply( + return (T) reservation.animationTransition.apply( // ease from.styles.get(key), to.styles.get(key), to.easing, delta); } diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index 1ea7c301..fd2ffbe3 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -17,6 +17,7 @@ public class CompiledAnimation { public final VeraApp app; public final String name; + public final VLoopMode loopMode; public final VEasing unwindEasing; public final int unwindTime; @@ -41,6 +42,7 @@ public class CompiledAnimation { protected CompiledAnimation( VeraApp app, String name, int duration, + VLoopMode loopMode, VEasing unwindEasing, int unwindTime, List keyframes, List keys ) { @@ -48,6 +50,7 @@ protected CompiledAnimation( this.name = name; this.duration = duration; + this.loopMode = loopMode; this.unwindEasing = unwindEasing; this.unwindTime = unwindTime; this.keyframes = keyframes; @@ -77,7 +80,7 @@ public int getKeyframeIndexAtTime(int time) { return keyframes.size() - 1; } - public int getKeyframeTimeAt(VKeyframe keyframe) { + public int getWhenKeyframe(VKeyframe keyframe) { int buffer = 0; for (VKeyframe frame : keyframes) { @@ -89,7 +92,11 @@ public int getKeyframeTimeAt(VKeyframe keyframe) { } public float getKeyframeDelta(int time, VKeyframe from, VKeyframe to) { - int fromTime = getKeyframeTimeAt(from) + from.transitionTime + from.stayTime; + return getKeyframeDelta(time, getWhenKeyframe(from), from, to); + } + + public float getKeyframeDelta(int time, int whenFrom, VKeyframe from, VKeyframe to) { + int fromTime = whenFrom + from.transitionTime + from.stayTime; int toTime = fromTime + to.transitionTime; if (time > toTime) return 1f; diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index a7efa331..dc59b950 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -11,7 +11,12 @@ public PlaybackContext(CompiledAnimation animation, long startTime) { } public int getRelativeTime() { - return Math.toIntExact(System.currentTimeMillis() - startTime) % animation.duration; // TODO: loop modes + int relative = Math.toIntExact(System.currentTimeMillis() - startTime); + + return switch (animation.loopMode) { + case NONE -> relative; + case FORWARD_REPEAT -> relative % animation.duration; + }; } public float getProgress() { diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 671fb292..850903a2 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -13,13 +13,15 @@ public class VAnimation { public String name; + public VLoopMode loopMode; public List keyframes; public VEasing unwindEasing; public int unwindTime; - public VAnimation(String name, VEasing unwindEasing, int unwindTime, List keyframes) { + public VAnimation(String name, VLoopMode loopMode, VEasing unwindEasing, int unwindTime, List keyframes) { this.name = name.toLowerCase(); + this.loopMode = loopMode; this.keyframes = keyframes; this.unwindEasing = unwindEasing; this.unwindTime = unwindTime; @@ -64,9 +66,20 @@ public CompiledAnimation compile(VeraApp app, VWidget widget) { extendedFrames.add(frame); } + if (loopMode == VLoopMode.FORWARD_REPEAT) { // add immediate end for smooth transition + VKeyframe frame = new VKeyframe( + keyframes.get(keyframes.size() - 1), + 0, + 0, + VEasings.IMMEDIATE + ); + extendedFrames.add(frame); + } + return new CompiledAnimation( app, name, index.totalDuration, + loopMode, unwindEasing, unwindTime, extendedFrames, index.styles ); @@ -81,6 +94,7 @@ public static class Builder { private final String name; private final List keyframes = new ArrayList<>(); + private VLoopMode loopMode = VLoopMode.NONE; private VEasing unwindEasing = VEasings.LINEAR; private int unwindTime = 0; @@ -104,6 +118,11 @@ public Builder keyframe(int transitionMs, int stayMs, Consumer apply, return this; } + public Builder loopMode(VLoopMode mode) { + this.loopMode = mode; + return this; + } + public Builder unwindEasing(VEasing easing) { this.unwindEasing = easing; return this; @@ -115,7 +134,7 @@ public Builder unwindTime(int ms) { } public VAnimation build() { - return new VAnimation(name, unwindEasing, unwindTime, keyframes); + return new VAnimation(name, loopMode, unwindEasing, unwindTime, keyframes); } } } diff --git a/src/main/java/net/snackbag/vera/style/animation/VLoopMode.java b/src/main/java/net/snackbag/vera/style/animation/VLoopMode.java new file mode 100644 index 00000000..24ee087b --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/VLoopMode.java @@ -0,0 +1,8 @@ +package net.snackbag.vera.style.animation; + +public enum VLoopMode { + NONE, + FORWARD_REPEAT, + + // TODO: reverse repeat +} From 067610ba083d511d26ebdde556fa5b3182d12a8b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:49:10 +0100 Subject: [PATCH 522/661] implement winding functionality --- .../mcvera/test/StyleTestApplication.java | 6 ++-- .../vera/style/animation/AnimationEngine.java | 21 +++++++++++- .../vera/style/animation/PlaybackContext.java | 32 +++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 2c5754e1..15a4fe14 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -21,6 +21,8 @@ public class StyleTestApplication extends VeraApp { .build(); private final VAnimation longTestAnimation = new VAnimation.Builder("long_test") .loopMode(VLoopMode.FORWARD_REPEAT) + .unwindTime(1000) + .keyframe(1000, 2000, frame -> { frame.style("background-color", VColor.MC_RED); }) @@ -44,11 +46,11 @@ public void init() { VRect testRect = new VRect(VColor.black(), this).alsoAdd(); testRect.onLeftClick(() -> { - testRect.animations.start(longTestAnimation); + testRect.animations.startOrRewind(longTestAnimation); }); testRect.onRightClick(() -> { - testRect.animations.stop(longTestAnimation); + testRect.animations.unwind(longTestAnimation); }); } diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 8af84a87..9e03cdc6 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -25,6 +25,11 @@ public void start(VAnimation animation) { active.put(compiled.name, new PlaybackContext(compiled, System.currentTimeMillis())); } + public void startOrRewind(VAnimation animation) { + if (active.containsKey(animation.name)) active.get(animation.name).rewind(); + else start(animation); + } + public void stop(VAnimation animation) { stop(animation.name); } @@ -38,6 +43,15 @@ public void stop(String name) { active.remove(name); } + public void unwind(VAnimation animation) { + unwind(animation.name); + } + + public void unwind(String name) { + if (active.containsKey(name)) active.get(name).unwind(); + else MinecraftVera.LOGGER.warn("Couldn't unwind %s, because it's not active".formatted(name)); + } + public T animateStyle(String key, T value) { for (PlaybackContext ctx : active.values()) { CompiledAnimation animation = ctx.animation; @@ -68,9 +82,14 @@ public T animateStyle(String key, T value) { float delta = animation.getKeyframeDelta(time, fromKfWhen, from, to); StyleValueType reservation = widget.app.styleSheet.getReservation(key); - return (T) reservation.animationTransition.apply( // ease + T kfEase = (T) reservation.animationTransition.apply( // ease keyframe transition from.styles.get(key), to.styles.get(key), to.easing, delta); + T windingEase = (T) reservation.animationTransition.apply( // ease winding + kfEase, widget.app.styleSheet.getKey(widget, key), + animation.unwindEasing, ctx.getWindingProgress() + ); + return windingEase; } return value; diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index dc59b950..8054c0d3 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -1,10 +1,16 @@ package net.snackbag.vera.style.animation; +import net.minecraft.util.math.MathHelper; + // TODO: winding public class PlaybackContext { public final CompiledAnimation animation; public final long startTime; + private float prevWindingProgress = 0f; + private long windingStartTime = -1; + private boolean unwindingOrRewinding = false; // unwinding = false; rewinding = true + public PlaybackContext(CompiledAnimation animation, long startTime) { this.animation = animation; this.startTime = startTime; @@ -26,4 +32,30 @@ public float getProgress() { public int getCurrentLoopNumber() { return (int) Math.ceil((float) (System.currentTimeMillis() - startTime) / animation.duration); } + + /** + * 0 = animation has full impact, 1 = animation has no impact + * @return the current winding progress from 0 to 1 + */ + public float getWindingProgress() { + if (windingStartTime == -1) return 0f; + + float delta = (float) (System.currentTimeMillis() - windingStartTime) / animation.unwindTime; + if (unwindingOrRewinding) return MathHelper.clamp(prevWindingProgress - delta, 0f, 1f); // is rewinding + else return MathHelper.clamp(prevWindingProgress + delta, 0f, 1f); // is unwinding + } + + public void unwind() { + prevWindingProgress = getWindingProgress(); + windingStartTime = System.currentTimeMillis(); + unwindingOrRewinding = false; + } + + public void rewind() { + if (windingStartTime == -1) return; + + prevWindingProgress = getWindingProgress(); + windingStartTime = System.currentTimeMillis(); + unwindingOrRewinding = true; + } } From b785412b9d2e52fcfe76f9437d3f72f06afc1864 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:53:58 +0100 Subject: [PATCH 523/661] manage winding lifetimes --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 9e03cdc6..d4c08adb 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -98,12 +98,14 @@ public T animateStyle(String key, T value) { public void updateLifetimes() { for (Map.Entry entry : active.entrySet()) { PlaybackContext ctx = entry.getValue(); + String name = entry.getKey(); CompiledAnimation animation = ctx.animation; if (animation.loopMode == VLoopMode.NONE && ctx.getProgress() >= 1.0f) { - // TODO: unwinding - stop(entry.getKey()); + unwind(name); } + + if (ctx.getWindingProgress() >= 1.0f) stop(name); } } } From d43ddbb7b255307f04e16b16459ca911e530a065 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:54:09 +0100 Subject: [PATCH 524/661] dont double unwind/rewind --- .../net/snackbag/vera/style/animation/PlaybackContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index 8054c0d3..79aa7e15 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -46,13 +46,15 @@ public float getWindingProgress() { } public void unwind() { + if (!unwindingOrRewinding) return; // if already unwinding + prevWindingProgress = getWindingProgress(); windingStartTime = System.currentTimeMillis(); unwindingOrRewinding = false; } public void rewind() { - if (windingStartTime == -1) return; + if (windingStartTime == -1 || unwindingOrRewinding) return; // if not unwinding OR if already rewinding prevWindingProgress = getWindingProgress(); windingStartTime = System.currentTimeMillis(); From 01c1766d2e52c48f0a8e1b0d0a422d6948d192da Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:55:11 +0100 Subject: [PATCH 525/661] fix stopping unwind --- .../java/net/snackbag/vera/style/animation/PlaybackContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index 79aa7e15..268701c4 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -46,7 +46,7 @@ public float getWindingProgress() { } public void unwind() { - if (!unwindingOrRewinding) return; // if already unwinding + if (!(unwindingOrRewinding && windingStartTime != -1)) return; // if already unwinding prevWindingProgress = getWindingProgress(); windingStartTime = System.currentTimeMillis(); From 0801fc84c75ac61f831ac2508d204de7dbf1af97 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 17:55:11 +0100 Subject: [PATCH 526/661] fix stopping unwind --- .../java/net/snackbag/vera/style/animation/PlaybackContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index 79aa7e15..42d16b7b 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -46,7 +46,7 @@ public float getWindingProgress() { } public void unwind() { - if (!unwindingOrRewinding) return; // if already unwinding + if (!unwindingOrRewinding && windingStartTime != -1) return; // if already unwinding prevWindingProgress = getWindingProgress(); windingStartTime = System.currentTimeMillis(); From 0f3e4278b12db81f5ba2d2a461b7000a5955525f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 18:08:51 +0100 Subject: [PATCH 527/661] remove winding todo --- .../java/net/snackbag/vera/style/animation/PlaybackContext.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index 42d16b7b..fcfc2f46 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -2,7 +2,6 @@ import net.minecraft.util.math.MathHelper; -// TODO: winding public class PlaybackContext { public final CompiledAnimation animation; public final long startTime; From ab71b38aae19209fc2650412a5dfc2982da13d5d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 18:13:01 +0100 Subject: [PATCH 528/661] add QOL animation methods to VWidget --- .../java/net/snackbag/vera/widget/VWidget.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 80f3f083..1f6637ff 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -65,12 +65,10 @@ public void setStyle(String key, StyleState state, V... value) { } public V getStyle(String key) { -// return app.styleSheet.getKey(this, key); return animations.animateStyle(key, app.styleSheet.getKey(this, key)); } public V getStyle(String key, StyleState state) { -// return app.styleSheet.getKey(this, key, state); return animations.animateStyle(key, app.styleSheet.getKey(this, key, state)); } @@ -84,6 +82,22 @@ public V getStyleOrDefault(String key, V dflt, StyleState state) { return style != null ? style : dflt; } + public void animate(VAnimation animation) { + animations.start(animation); + } + + public void stopAnimation(String animation) { + animations.stop(animation); + } + + public void startOrRewindAnimation(VAnimation animation) { + animations.startOrRewind(animation); + } + + public void unwindAnimation(VAnimation animation) { + animations.unwind(animation); + } + public StyleState createStyleState() { return createStyleState(true); } From 4b59874a57a4d6d03cc52c0b2a3bc07e297182a8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 19:23:37 +0100 Subject: [PATCH 529/661] reset winding --- .../snackbag/vera/style/animation/AnimationEngine.java | 1 + .../snackbag/vera/style/animation/PlaybackContext.java | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index d4c08adb..e7fbfe99 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -102,6 +102,7 @@ public void updateLifetimes() { CompiledAnimation animation = ctx.animation; if (animation.loopMode == VLoopMode.NONE && ctx.getProgress() >= 1.0f) { + ctx.potentiallyResetWinding(); unwind(name); } diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index fcfc2f46..b43cd220 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -59,4 +59,12 @@ public void rewind() { windingStartTime = System.currentTimeMillis(); unwindingOrRewinding = true; } + + public void potentiallyResetWinding() { + if (getWindingProgress() > 0.0f) return; + + prevWindingProgress = 0f; + windingStartTime = -1; + unwindingOrRewinding = false; + } } From ac3e32dd56e957c8b35a81bc9e7a11838c2207cc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 19:24:25 +0100 Subject: [PATCH 530/661] add relative unwinding --- .../snackbag/vera/style/animation/AnimationEngine.java | 3 ++- .../snackbag/vera/style/animation/PlaybackContext.java | 10 ++++++++-- .../net/snackbag/vera/style/animation/VAnimation.java | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index e7fbfe99..1701aad5 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -101,8 +101,9 @@ public void updateLifetimes() { String name = entry.getKey(); CompiledAnimation animation = ctx.animation; - if (animation.loopMode == VLoopMode.NONE && ctx.getProgress() >= 1.0f) { ctx.potentiallyResetWinding(); + + if (animation.unwindAtEnd && animation.loopMode == VLoopMode.NONE && ctx.getProgress() >= 1.0f) { unwind(name); } diff --git a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java index b43cd220..2ef35253 100644 --- a/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java +++ b/src/main/java/net/snackbag/vera/style/animation/PlaybackContext.java @@ -8,6 +8,7 @@ public class PlaybackContext { private float prevWindingProgress = 0f; private long windingStartTime = -1; + private int unwindStartRelativeTime = -1; // for the relative unwinding private boolean unwindingOrRewinding = false; // unwinding = false; rewinding = true public PlaybackContext(CompiledAnimation animation, long startTime) { @@ -39,16 +40,20 @@ public int getCurrentLoopNumber() { public float getWindingProgress() { if (windingStartTime == -1) return 0f; - float delta = (float) (System.currentTimeMillis() - windingStartTime) / animation.unwindTime; + int unwindTime = animation.unwindTime; + if (unwindTime == -1) unwindTime = unwindStartRelativeTime; + + float delta = (float) (System.currentTimeMillis() - windingStartTime) / unwindTime; if (unwindingOrRewinding) return MathHelper.clamp(prevWindingProgress - delta, 0f, 1f); // is rewinding else return MathHelper.clamp(prevWindingProgress + delta, 0f, 1f); // is unwinding } public void unwind() { if (!unwindingOrRewinding && windingStartTime != -1) return; // if already unwinding + if (windingStartTime == -1) unwindStartRelativeTime = getRelativeTime(); prevWindingProgress = getWindingProgress(); - windingStartTime = System.currentTimeMillis(); + windingStartTime = System.currentTimeMillis() - 1; unwindingOrRewinding = false; } @@ -65,6 +70,7 @@ public void potentiallyResetWinding() { prevWindingProgress = 0f; windingStartTime = -1; + unwindStartRelativeTime = -1; unwindingOrRewinding = false; } } diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 850903a2..ca4f8baf 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -133,6 +133,12 @@ public Builder unwindTime(int ms) { return this; } + public Builder relativeUnwindTime() { + this.unwindTime = -1; + return this; + } + + public VAnimation build() { return new VAnimation(name, loopMode, unwindEasing, unwindTime, keyframes); } From f60ad55f80731aef9805c6dc13e964cfc6d50a5a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 19:25:20 +0100 Subject: [PATCH 531/661] add option for unwinding at end --- .../vera/style/animation/AnimationEngine.java | 2 +- .../vera/style/animation/CompiledAnimation.java | 8 +++++--- .../snackbag/vera/style/animation/VAnimation.java | 13 ++++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 1701aad5..eae1a66f 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -107,7 +107,7 @@ public void updateLifetimes() { unwind(name); } - if (ctx.getWindingProgress() >= 1.0f) stop(name); + if (ctx.getWindingProgress() >= 1.0f || (!animation.unwindAtEnd && ctx.getProgress() >= 1.0f)) stop(name); } } } diff --git a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java index fd2ffbe3..2896c111 100644 --- a/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/CompiledAnimation.java @@ -20,6 +20,7 @@ public class CompiledAnimation { public final VLoopMode loopMode; public final VEasing unwindEasing; public final int unwindTime; + public final boolean unwindAtEnd; /** * Sum of all keyframes's transition time + stay time @@ -43,7 +44,7 @@ protected CompiledAnimation( VeraApp app, String name, int duration, VLoopMode loopMode, - VEasing unwindEasing, int unwindTime, + VEasing unwindEasing, int unwindTime, boolean unwindAtEnd, List keyframes, List keys ) { this.app = app; @@ -53,14 +54,15 @@ protected CompiledAnimation( this.loopMode = loopMode; this.unwindEasing = unwindEasing; this.unwindTime = unwindTime; + this.unwindAtEnd = unwindAtEnd; this.keyframes = keyframes; this.keys = keys; } @Override public String toString() { - return "CompiledAnimation{name='%s', duration='%s', unwindEasing='%s', unwindTime=%s, keyframeCount=%s}" - .formatted(name, duration, unwindEasing, unwindTime, keyframes.size()); + return "CompiledAnimation{name='%s', duration='%s', loopMode='%s' unwindEasing='%s', unwindTime=%s, unwindAtEnd=%s, keyframeCount=%s}" + .formatted(name, duration, loopMode, unwindEasing, unwindTime, unwindAtEnd, keyframes.size()); } public int getKeyframeIndexAtTime(int time) { diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index ca4f8baf..b6772e65 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -18,13 +18,15 @@ public class VAnimation { public VEasing unwindEasing; public int unwindTime; + public boolean unwindAtEnd; - public VAnimation(String name, VLoopMode loopMode, VEasing unwindEasing, int unwindTime, List keyframes) { + public VAnimation(String name, VLoopMode loopMode, VEasing unwindEasing, int unwindTime, boolean unwindAtEnd, List keyframes) { this.name = name.toLowerCase(); this.loopMode = loopMode; this.keyframes = keyframes; this.unwindEasing = unwindEasing; this.unwindTime = unwindTime; + this.unwindAtEnd = unwindAtEnd; } public CompiledAnimation compile(VeraApp app, VWidget widget) { @@ -80,7 +82,7 @@ public CompiledAnimation compile(VeraApp app, VWidget widget) { app, name, index.totalDuration, loopMode, - unwindEasing, unwindTime, + unwindEasing, unwindTime, unwindAtEnd, extendedFrames, index.styles ); } @@ -97,6 +99,7 @@ public static class Builder { private VLoopMode loopMode = VLoopMode.NONE; private VEasing unwindEasing = VEasings.LINEAR; private int unwindTime = 0; + private boolean unwindAtEnd = false; public Builder(String name) { this.name = name; @@ -138,9 +141,13 @@ public Builder relativeUnwindTime() { return this; } + public Builder unwindAtEnd() { + this.unwindAtEnd = true; + return this; + } public VAnimation build() { - return new VAnimation(name, loopMode, unwindEasing, unwindTime, keyframes); + return new VAnimation(name, loopMode, unwindEasing, unwindTime, unwindAtEnd, keyframes); } } } From 1b90415cf09ade8911c96c5965f3bcc200fec99a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:32:56 +0100 Subject: [PATCH 532/661] add rewind method --- .../vera/style/animation/AnimationEngine.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index eae1a66f..262e8cbd 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -26,7 +26,7 @@ public void start(VAnimation animation) { } public void startOrRewind(VAnimation animation) { - if (active.containsKey(animation.name)) active.get(animation.name).rewind(); + if (active.containsKey(animation.name)) rewind(animation); else start(animation); } @@ -52,6 +52,26 @@ public void unwind(String name) { else MinecraftVera.LOGGER.warn("Couldn't unwind %s, because it's not active".formatted(name)); } + public void rewind(VAnimation animation) { + rewind(animation.name); + } + + public void rewind(String name) { + if (!active.containsKey(name)) { + MinecraftVera.LOGGER.warn("Couldn't rewind %s, because it's not active".formatted(name)); + return; + } + + PlaybackContext ctx = active.get(name); + if (ctx.getWindingProgress() <= 0.0f) { + MinecraftVera.LOGGER.warn("Couldn't rewind %s, because it's not unwinding".formatted(name)); + return; + } + + ctx.rewind(); + widget.events.fire(VEvents.Animation.REWIND_BEGIN, ctx.animation); + } + public T animateStyle(String key, T value) { for (PlaybackContext ctx : active.values()) { CompiledAnimation animation = ctx.animation; From a404e27bf4b602b3e11f536eec2be02746356b5a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:33:10 +0100 Subject: [PATCH 533/661] call unwind begin event --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 262e8cbd..5d10de59 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -48,7 +48,10 @@ public void unwind(VAnimation animation) { } public void unwind(String name) { - if (active.containsKey(name)) active.get(name).unwind(); + if (active.containsKey(name)) { + active.get(name).unwind(); + widget.events.fire(VEvents.Animation.UNWIND_BEGIN, active.get(name).animation); + } else MinecraftVera.LOGGER.warn("Couldn't unwind %s, because it's not active".formatted(name)); } From 7e85660f08377e0772631082c7831feaee98db7e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:33:22 +0100 Subject: [PATCH 534/661] call animation begin & finish events --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 5d10de59..db151c49 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -23,6 +23,7 @@ public void start(VAnimation animation) { CompiledAnimation compiled = animation.compile(widget.app, widget); active.put(compiled.name, new PlaybackContext(compiled, System.currentTimeMillis())); + widget.events.fire(VEvents.Animation.BEGIN, animation); } public void startOrRewind(VAnimation animation) { @@ -40,6 +41,8 @@ public void stop(String name) { return; } + PlaybackContext ctx = active.get(name); + widget.events.fire(VEvents.Animation.FINISH, ctx.animation, ctx.startTime); active.remove(name); } From 66ed6c7c6bf9007f262b5f81bae59c3d08de980e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:33:46 +0100 Subject: [PATCH 535/661] add QOL isActive & getActive methods --- .../snackbag/vera/style/animation/AnimationEngine.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index db151c49..c859d1a9 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -1,11 +1,13 @@ package net.snackbag.vera.style.animation; import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.vera.event.VEvents; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.widget.VWidget; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class AnimationEngine { public final VWidget widget; @@ -136,4 +138,12 @@ public void updateLifetimes() { if (ctx.getWindingProgress() >= 1.0f || (!animation.unwindAtEnd && ctx.getProgress() >= 1.0f)) stop(name); } } + + public boolean isActive(String animation) { + return active.containsKey(animation.toLowerCase()); + } + + public Set getActive() { + return active.keySet(); + } } From f8a366a3d237747f87898bec9cee5257d3587a3f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:34:08 +0100 Subject: [PATCH 536/661] use entry value as winding value instead of stylesheet --- .../java/net/snackbag/vera/style/animation/AnimationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index c859d1a9..71196e22 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -114,7 +114,7 @@ public T animateStyle(String key, T value) { from.styles.get(key), to.styles.get(key), to.easing, delta); T windingEase = (T) reservation.animationTransition.apply( // ease winding - kfEase, widget.app.styleSheet.getKey(widget, key), + kfEase, value, animation.unwindEasing, ctx.getWindingProgress() ); return windingEase; From 05d10282041d4a36468c675ece750c5273a558b6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:34:19 +0100 Subject: [PATCH 537/661] dont stop animation while winding --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 71196e22..cfd63d85 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -135,7 +135,9 @@ public void updateLifetimes() { unwind(name); } - if (ctx.getWindingProgress() >= 1.0f || (!animation.unwindAtEnd && ctx.getProgress() >= 1.0f)) stop(name); + if (ctx.getWindingProgress() >= 1.0f || (!animation.unwindAtEnd && ctx.getProgress() >= 1.0f && (ctx.getWindingProgress() == 0.0f || ctx.getWindingProgress() == 1.0f))) { + stop(name); + } } } From ac9cfd8d636c8a5aec0d04651fea7f56c4675ba2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:34:33 +0100 Subject: [PATCH 538/661] add animation transition name variable --- src/main/java/net/snackbag/vera/style/animation/VAnimation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index b6772e65..4c6c31ce 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -12,6 +12,7 @@ import java.util.function.Consumer; public class VAnimation { + public static final String INTERNAL_TRANSITION_NAME = "internal-vera-transition"; public String name; public VLoopMode loopMode; public List keyframes; From 03fe990b2d5eb11ce5dfbcf1fc3969bdd7ac9d09 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:35:08 +0100 Subject: [PATCH 539/661] add more animation QOL methods --- .../net/snackbag/vera/widget/VWidget.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 1f6637ff..866f5a03 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -83,21 +83,56 @@ public V getStyleOrDefault(String key, V dflt, StyleState state) { } public void animate(VAnimation animation) { + animate(animation, false); + } + + public void animate(VAnimation animation, boolean override) { + if (override && isAnimationActive(animation.name)) stopAnimation(animation); animations.start(animation); } + public void stopAnimation(VAnimation animation) { + stopAnimation(animation.name); + } + public void stopAnimation(String animation) { animations.stop(animation); } + public void stopAllAnimations() { + for (String animation : animations.getActive()) { + stopAnimation(animation); + } + } + public void startOrRewindAnimation(VAnimation animation) { animations.startOrRewind(animation); } public void unwindAnimation(VAnimation animation) { + unwindAnimation(animation.name); + } + + public void unwindAnimation(String animation) { animations.unwind(animation); } + public void rewindAnimation(VAnimation animation) { + rewindAnimation(animation.name); + } + + public void rewindAnimation(String animation) { + animations.rewind(animation); + } + + public boolean isAnimationActive(VAnimation animation) { + return isAnimationActive(animation.name); + } + + public boolean isAnimationActive(String animation) { + return animations.isActive(animation); + } + public StyleState createStyleState() { return createStyleState(true); } From e8488cc5e13f83e0feee5fcda417dd362e2e0f81 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:35:18 +0100 Subject: [PATCH 540/661] handle transitions --- .../net/snackbag/vera/widget/VWidget.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 866f5a03..35816f41 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -9,7 +9,9 @@ import net.snackbag.vera.layout.VLayout; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.style.animation.AnimationEngine; +import net.snackbag.vera.style.animation.CompiledAnimation; import net.snackbag.vera.style.animation.VAnimation; +import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.util.DragHandler; import java.nio.file.Path; @@ -28,6 +30,9 @@ public abstract class VWidget> extends VElement { private StyleState handledPrevStyleState = StyleState.DEFAULT; // constantly updates private StyleState prevStyleState = StyleState.DEFAULT; // updates max once per frame, can be seen as the definite result + private StyleState transitionOrigin = null; + private boolean isTransitionUnwinding = false; + public final LinkedHashSet classes = new LinkedHashSet<>(); public VWidget(int x, int y, int width, int height, VeraApp app) { @@ -199,6 +204,36 @@ public void beforeRender() { StyleState state = createStyleState(); if (state != prevStyleState) { + Integer transitionTime = app.styleSheet.getKey(this, "transition", state); + VEasing transitionEasing = app.styleSheet.getKey(this, "transition-easing", state); + + Transition: if (transitionTime > 0) { + if (transitionOrigin == state) { + if (isTransitionUnwinding) rewindAnimation(VAnimation.INTERNAL_TRANSITION_NAME); + else unwindAnimation(VAnimation.INTERNAL_TRANSITION_NAME); + break Transition; + } + + VAnimation.Builder builder = new VAnimation.Builder(VAnimation.INTERNAL_TRANSITION_NAME) + .relativeUnwindTime() + .unwindEasing(transitionEasing); + + builder.keyframe(0, 1, frame -> { + for (String key : app.styleSheet.getKeysStacked(this, prevStyleState)) { + frame.style(key, getStyle(key, prevStyleState)); + } + }); + + builder.keyframe(transitionTime - 1, 0, frame -> { + for (String key : app.styleSheet.getKeysStacked(this, state)) { + frame.style(key, app.styleSheet.getKey(this, key, state)); + } + }); + + transitionOrigin = prevStyleState; + animate(builder.build(), true); + } + prevStyleState = state; } } @@ -350,6 +385,16 @@ public void handleBuiltinEvent(String event, Object... args) { clearRightClickDown(); clearMiddleClickDown(); } + + case VEvents.Animation.FINISH -> { + CompiledAnimation animation = (CompiledAnimation) args[0]; + if (animation.name.equals(VAnimation.INTERNAL_TRANSITION_NAME)) { + transitionOrigin = null; + isTransitionUnwinding = false; + } + } + case VEvents.Animation.UNWIND_BEGIN -> isTransitionUnwinding = true; + case VEvents.Animation.REWIND_BEGIN -> isTransitionUnwinding = false; } } From be4906e53990acf846381ae5f196fb54265ca38d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:39:06 +0100 Subject: [PATCH 541/661] fix concurrency crash with multiple animations at once --- .../net/snackbag/vera/style/animation/AnimationEngine.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index cfd63d85..99317157 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -124,7 +124,9 @@ public T animateStyle(String key, T value) { } public void updateLifetimes() { - for (Map.Entry entry : active.entrySet()) { + HashMap copies = new HashMap<>(active); // fix concurrency crash + + for (Map.Entry entry : copies.entrySet()) { PlaybackContext ctx = entry.getValue(); String name = entry.getKey(); CompiledAnimation animation = ctx.animation; From 0d6e2df37f98123449e2c94596279b2569e0c51f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sun, 22 Feb 2026 22:42:44 +0100 Subject: [PATCH 542/661] test transitions --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 15a4fe14..076bcf43 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -52,6 +52,10 @@ public void init() { testRect.onRightClick(() -> { testRect.animations.unwind(longTestAnimation); }); + + testRect.setStyle("transition", 100); + testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); + testRect.setStyle("background-color", StyleState.CLICKED, VColor.MC_RED); } public VStyleSheet createStyleSheet() { From 367e9f3af91ceb5d4a47f8d87399b16c05b9be57 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 15:40:59 +0100 Subject: [PATCH 543/661] clean up --- .../mcvera/test/StyleTestApplication.java | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 076bcf43..fac219dd 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -15,23 +15,15 @@ public class StyleTestApplication extends VeraApp { public static StyleTestApplication INSTANCE = new StyleTestApplication(); private final VAnimation testAnimation = new VAnimation.Builder("test") - .keyframe(1000, 5000, frame -> { - frame.style("background-color", VColor.MC_RED); - }) + .keyframe(1000, 5000, frame -> frame.style("background-color", VColor.MC_RED)) .build(); private final VAnimation longTestAnimation = new VAnimation.Builder("long_test") .loopMode(VLoopMode.FORWARD_REPEAT) .unwindTime(1000) - .keyframe(1000, 2000, frame -> { - frame.style("background-color", VColor.MC_RED); - }) - .keyframe(1000, 5000, frame -> { - frame.style("background-color", VColor.MC_GOLD); - }) - .keyframe(1000, 1000, frame -> { - frame.style("background-color", VColor.MC_WHITE); - }) + .keyframe(1000, 2000, frame -> frame.style("background-color", VColor.MC_RED)) + .keyframe(1000, 5000, frame -> frame.style("background-color", VColor.MC_GOLD)) + .keyframe(1000, 1000, frame -> frame.style("background-color", VColor.MC_WHITE)) .build(); @Override @@ -45,13 +37,9 @@ public void init() { .alsoAdd(); VRect testRect = new VRect(VColor.black(), this).alsoAdd(); - testRect.onLeftClick(() -> { - testRect.animations.startOrRewind(longTestAnimation); - }); - testRect.onRightClick(() -> { - testRect.animations.unwind(longTestAnimation); - }); + testRect.onLeftClick(() -> testRect.animations.startOrRewind(longTestAnimation)); + testRect.onRightClick(() -> testRect.animations.unwind(longTestAnimation)); testRect.setStyle("transition", 100); testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); From 8b31c59e950264e1d649a4ea8fcc8b410bb6b560 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 15:44:26 +0100 Subject: [PATCH 544/661] remove V prefix from easing classes as they are not meant to be interacted with --- .../easing/{VImmediateEasing.java => ImmediateEasing.java} | 4 ++-- .../easing/{VLinearEasing.java => LinearEasing.java} | 4 ++-- .../net/snackbag/vera/style/animation/easing/VEasings.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/main/java/net/snackbag/vera/style/animation/easing/{VImmediateEasing.java => ImmediateEasing.java} (77%) rename src/main/java/net/snackbag/vera/style/animation/easing/{VLinearEasing.java => LinearEasing.java} (83%) diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/ImmediateEasing.java similarity index 77% rename from src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java rename to src/main/java/net/snackbag/vera/style/animation/easing/ImmediateEasing.java index 3260443f..65e4867c 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VImmediateEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/ImmediateEasing.java @@ -1,7 +1,7 @@ package net.snackbag.vera.style.animation.easing; -public class VImmediateEasing extends VEasing { - public VImmediateEasing() { +public class ImmediateEasing extends VEasing { + public ImmediateEasing() { super("immediate"); } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java similarity index 83% rename from src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java rename to src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java index 3d71e655..d6f2ee27 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VLinearEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java @@ -1,7 +1,7 @@ package net.snackbag.vera.style.animation.easing; -public class VLinearEasing extends VEasing { - protected VLinearEasing() { +public class LinearEasing extends VEasing { + protected LinearEasing() { super("linear"); } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index f473a696..05f1bc0d 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -3,8 +3,8 @@ import net.snackbag.vera.Vera; public class VEasings { - public static final VImmediateEasing IMMEDIATE = new VImmediateEasing(); - public static final VLinearEasing LINEAR = new VLinearEasing(); + public static final ImmediateEasing IMMEDIATE = new ImmediateEasing(); + public static final LinearEasing LINEAR = new LinearEasing(); public static VEasing getIgnoreCase(String name) { return Vera.registrar.getEasingIgnoreCase(name); From ac5c00e2433d6c740f61233afeaef676ef732451 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 15:59:25 +0100 Subject: [PATCH 545/661] generalise int apply method --- .../snackbag/vera/style/animation/easing/LinearEasing.java | 6 ------ .../net/snackbag/vera/style/animation/easing/VEasing.java | 4 +++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java index d6f2ee27..e728b094 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/LinearEasing.java @@ -9,10 +9,4 @@ protected LinearEasing() { public float apply(float from, float to, float delta) { return from + delta * (to - from); } - - @Override - public int apply(int from, int to, float delta) { - final float fromF = (float) from; - return Math.round(fromF + delta * ((float) to - fromF)); - } } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java index 3e356ba7..b8025176 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasing.java @@ -9,5 +9,7 @@ public VEasing(String name) { } public abstract float apply(float from, float to, float delta); - public abstract int apply(int from, int to, float delta); + public int apply(int from, int to, float delta) { + return Math.round(apply((float) from, (float) to, delta)); + } } From 78045be199493586899e1086404e65227944047c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 16:00:17 +0100 Subject: [PATCH 546/661] add getDefault method for easings --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 2 +- .../java/net/snackbag/vera/style/animation/VAnimation.java | 6 +++--- .../net/snackbag/vera/style/animation/easing/VEasings.java | 4 ++++ .../snackbag/vera/style/standard/WidgetStandardStyle.java | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index c71aa270..206679d1 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -22,7 +22,7 @@ public enum StyleValueType { .withSize(easing.apply(from.getSize(), to.getSize(), delta)) .withName(delta > 0.5 ? to.getName() : from.getName())), CURSOR(VCursorShape.DEFAULT, (f, t, e, d) -> d > 0.5 ? t : f), - EASING(VEasings.LINEAR, (f, t, e, d) -> d > 0.5 ? t : f), + EASING(VEasings.getDefault(), (f, t, e, d) -> d > 0.5 ? t : f), V4INT(new V4Int(0), (from, to, easing, delta) -> new V4Int( easing.apply(from.get1(), to.get1(), delta), diff --git a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java index 4c6c31ce..e0beecb2 100644 --- a/src/main/java/net/snackbag/vera/style/animation/VAnimation.java +++ b/src/main/java/net/snackbag/vera/style/animation/VAnimation.java @@ -98,7 +98,7 @@ public static class Builder { private final List keyframes = new ArrayList<>(); private VLoopMode loopMode = VLoopMode.NONE; - private VEasing unwindEasing = VEasings.LINEAR; + private VEasing unwindEasing = VEasings.getDefault(); private int unwindTime = 0; private boolean unwindAtEnd = false; @@ -107,11 +107,11 @@ public Builder(String name) { } public Builder keyframe(int stayMs, Consumer apply) { - return keyframe(0, stayMs, apply, VEasings.LINEAR); + return keyframe(0, stayMs, apply, VEasings.getDefault()); } public Builder keyframe(int transitionMs, int stayMs, Consumer apply) { - return keyframe(transitionMs, stayMs, apply, VEasings.LINEAR); + return keyframe(transitionMs, stayMs, apply, VEasings.getDefault()); } public Builder keyframe(int transitionMs, int stayMs, Consumer apply, VEasing easing) { diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index 05f1bc0d..156b19ac 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -6,6 +6,10 @@ public class VEasings { public static final ImmediateEasing IMMEDIATE = new ImmediateEasing(); public static final LinearEasing LINEAR = new LinearEasing(); + public static VEasing getDefault() { + return LINEAR; + } + public static VEasing getIgnoreCase(String name) { return Vera.registrar.getEasingIgnoreCase(name); } diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index ebd768ba..f909f7fd 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -21,7 +21,7 @@ public void apply(VStyleSheet sheet) { // Transition sheet.setKey(VWidget.class, "transition", 0); - sheet.setKey(VWidget.class, "transition-easing", VEasings.LINEAR); + sheet.setKey(VWidget.class, "transition-easing", VEasings.getDefault()); } @Override From 98b2fe5a04a371a033ba7bf83cce65e0c1ac711c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 16:01:06 +0100 Subject: [PATCH 547/661] add sine easings --- .../vera/style/animation/easing/VEasings.java | 7 +++++++ .../style/animation/easing/sine/SineInEasing.java | 15 +++++++++++++++ .../animation/easing/sine/SineInOutEasing.java | 15 +++++++++++++++ .../animation/easing/sine/SineOutEasing.java | 15 +++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index 156b19ac..eecb9783 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -1,11 +1,18 @@ package net.snackbag.vera.style.animation.easing; import net.snackbag.vera.Vera; +import net.snackbag.vera.style.animation.easing.sine.SineInEasing; +import net.snackbag.vera.style.animation.easing.sine.SineInOutEasing; +import net.snackbag.vera.style.animation.easing.sine.SineOutEasing; public class VEasings { public static final ImmediateEasing IMMEDIATE = new ImmediateEasing(); public static final LinearEasing LINEAR = new LinearEasing(); + public static final SineInEasing SIN_IN = new SineInEasing(); + public static final SineOutEasing SIN_OUT = new SineOutEasing(); + public static final SineInOutEasing SIN_IN_OUT = new SineInOutEasing(); + public static VEasing getDefault() { return LINEAR; } diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java new file mode 100644 index 00000000..67880e3e --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java @@ -0,0 +1,15 @@ +package net.snackbag.vera.style.animation.easing.sine; + +import net.minecraft.util.math.MathHelper; +import net.snackbag.vera.style.animation.easing.VEasing; + +public class SineInEasing extends VEasing { + public SineInEasing() { + super("sin-in"); + } + + @Override + public float apply(float from, float to, float delta) { + return (float) (1 - Math.cos((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); + } +} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java new file mode 100644 index 00000000..ac9714a6 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java @@ -0,0 +1,15 @@ +package net.snackbag.vera.style.animation.easing.sine; + +import net.minecraft.util.math.MathHelper; +import net.snackbag.vera.style.animation.easing.VEasing; + +public class SineInOutEasing extends VEasing { + public SineInOutEasing() { + super("sin-in-out"); + } + + @Override + public float apply(float from, float to, float delta) { + return (float) (-(Math.cos(Math.PI * MathHelper.lerp(delta, from, to)) - 1) / 2); + } +} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java new file mode 100644 index 00000000..98ed96f4 --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java @@ -0,0 +1,15 @@ +package net.snackbag.vera.style.animation.easing.sine; + +import net.minecraft.util.math.MathHelper; +import net.snackbag.vera.style.animation.easing.VEasing; + +public class SineOutEasing extends VEasing { + public SineOutEasing() { + super("sin-out"); + } + + @Override + public float apply(float from, float to, float delta) { + return (float) (Math.sin((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); + } +} From bc9bab0dca95261804d96b835da75b29af12be20 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 16:05:51 +0100 Subject: [PATCH 548/661] put all sine easings into one class instead of spreading them out --- .../style/animation/easing/SineEasing.java | 38 +++++++++++++++++++ .../vera/style/animation/easing/VEasings.java | 9 ++--- .../animation/easing/sine/SineInEasing.java | 15 -------- .../easing/sine/SineInOutEasing.java | 15 -------- .../animation/easing/sine/SineOutEasing.java | 15 -------- 5 files changed, 41 insertions(+), 51 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java delete mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java new file mode 100644 index 00000000..e2f699ca --- /dev/null +++ b/src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java @@ -0,0 +1,38 @@ +package net.snackbag.vera.style.animation.easing; + +import net.minecraft.util.math.MathHelper; + +public class SineEasing { + public static class In extends VEasing { + public In() { + super("sine-in"); + } + + @Override + public float apply(float from, float to, float delta) { + return (float) (1 - Math.cos((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); + } + } + + public static class Out extends VEasing { + public Out() { + super("sine-out"); + } + + @Override + public float apply(float from, float to, float delta) { + return (float) (Math.sin((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); + } + } + + public static class InOut extends VEasing { + public InOut() { + super("sine-in-out"); + } + + @Override + public float apply(float from, float to, float delta) { + return (float) (-(Math.cos(Math.PI * MathHelper.lerp(delta, from, to)) - 1) / 2); + } + } +} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index eecb9783..5af825a8 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -1,17 +1,14 @@ package net.snackbag.vera.style.animation.easing; import net.snackbag.vera.Vera; -import net.snackbag.vera.style.animation.easing.sine.SineInEasing; -import net.snackbag.vera.style.animation.easing.sine.SineInOutEasing; -import net.snackbag.vera.style.animation.easing.sine.SineOutEasing; public class VEasings { public static final ImmediateEasing IMMEDIATE = new ImmediateEasing(); public static final LinearEasing LINEAR = new LinearEasing(); - public static final SineInEasing SIN_IN = new SineInEasing(); - public static final SineOutEasing SIN_OUT = new SineOutEasing(); - public static final SineInOutEasing SIN_IN_OUT = new SineInOutEasing(); + public static final SineEasing.In SINE_IN = new SineEasing.In(); + public static final SineEasing.Out SINE_OUT = new SineEasing.Out(); + public static final SineEasing.InOut SINE_IN_OUT = new SineEasing.InOut(); public static VEasing getDefault() { return LINEAR; diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java deleted file mode 100644 index 67880e3e..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInEasing.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.snackbag.vera.style.animation.easing.sine; - -import net.minecraft.util.math.MathHelper; -import net.snackbag.vera.style.animation.easing.VEasing; - -public class SineInEasing extends VEasing { - public SineInEasing() { - super("sin-in"); - } - - @Override - public float apply(float from, float to, float delta) { - return (float) (1 - Math.cos((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java deleted file mode 100644 index ac9714a6..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineInOutEasing.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.snackbag.vera.style.animation.easing.sine; - -import net.minecraft.util.math.MathHelper; -import net.snackbag.vera.style.animation.easing.VEasing; - -public class SineInOutEasing extends VEasing { - public SineInOutEasing() { - super("sin-in-out"); - } - - @Override - public float apply(float from, float to, float delta) { - return (float) (-(Math.cos(Math.PI * MathHelper.lerp(delta, from, to)) - 1) / 2); - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java deleted file mode 100644 index 98ed96f4..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/easing/sine/SineOutEasing.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.snackbag.vera.style.animation.easing.sine; - -import net.minecraft.util.math.MathHelper; -import net.snackbag.vera.style.animation.easing.VEasing; - -public class SineOutEasing extends VEasing { - public SineOutEasing() { - super("sin-out"); - } - - @Override - public float apply(float from, float to, float delta) { - return (float) (Math.sin((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); - } -} From 764f4fdc5ddbe40bf10fbb6bcd65dec145a72bde Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 16:19:14 +0100 Subject: [PATCH 549/661] remove extra easings, something is weird --- .../style/animation/easing/SineEasing.java | 38 ------------------- .../vera/style/animation/easing/VEasings.java | 4 -- 2 files changed, 42 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java b/src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java deleted file mode 100644 index e2f699ca..00000000 --- a/src/main/java/net/snackbag/vera/style/animation/easing/SineEasing.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.snackbag.vera.style.animation.easing; - -import net.minecraft.util.math.MathHelper; - -public class SineEasing { - public static class In extends VEasing { - public In() { - super("sine-in"); - } - - @Override - public float apply(float from, float to, float delta) { - return (float) (1 - Math.cos((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); - } - } - - public static class Out extends VEasing { - public Out() { - super("sine-out"); - } - - @Override - public float apply(float from, float to, float delta) { - return (float) (Math.sin((MathHelper.lerp(delta, from, to) * Math.PI) / 2)); - } - } - - public static class InOut extends VEasing { - public InOut() { - super("sine-in-out"); - } - - @Override - public float apply(float from, float to, float delta) { - return (float) (-(Math.cos(Math.PI * MathHelper.lerp(delta, from, to)) - 1) / 2); - } - } -} diff --git a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java index 5af825a8..156b19ac 100644 --- a/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java +++ b/src/main/java/net/snackbag/vera/style/animation/easing/VEasings.java @@ -6,10 +6,6 @@ public class VEasings { public static final ImmediateEasing IMMEDIATE = new ImmediateEasing(); public static final LinearEasing LINEAR = new LinearEasing(); - public static final SineEasing.In SINE_IN = new SineEasing.In(); - public static final SineEasing.Out SINE_OUT = new SineEasing.Out(); - public static final SineEasing.InOut SINE_IN_OUT = new SineEasing.InOut(); - public static VEasing getDefault() { return LINEAR; } From f88ebc76d7f89fc524d707f6ec76c7e68fb45e79 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 16:25:08 +0100 Subject: [PATCH 550/661] add only xy constructor --- src/main/java/net/snackbag/vera/widget/VLabel.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 937a5e80..870e740f 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -19,9 +19,13 @@ public VLabel(String text, int x, int y, int width, int height, VeraApp app) { alignment = VHAlignmentFlag.LEFT; } + public VLabel(String text, int x, int y, VeraApp app) { + this(text, x, y, 100, 16, app); + adjustSize(); + } + public VLabel(String text, VeraApp app) { this(text, 0, 0, 100, 16, app); - adjustSize(); } From a9e2f6a1f61a14ca01ac06c440f0672167985952 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 16:52:21 +0100 Subject: [PATCH 551/661] Resolve #2 --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index fff8ce44..be4acadf 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -91,6 +91,8 @@ public void renderApp(VeraApp app) { } public void renderApps(VWindowPositioningFlag flag) { + if (!MinecraftClient.getInstance().isRunning()) return; + LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); List hierarchicApps = new ArrayList<>(); From 2d7055cd1d9a19ced24f6541f616b4096bfff912 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 17:20:24 +0100 Subject: [PATCH 552/661] fix stack overflow error due to --- src/main/java/net/snackbag/vera/style/VStyleSheet.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index 11bb778a..a92deb05 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -35,8 +35,11 @@ public class VStyleSheet { // if class contains key HashMap> mixed = mixClasses(widget.classes); - if (mixed.containsKey(key)) { - if (!mixed.get(key).containsKey(state)) return getKey(widget, key, state.fallback); + Contains: if (mixed.containsKey(key)) { + if (!mixed.get(key).containsKey(state)) { + if (state.fallback == null) break Contains; + return getKey(widget, key, state.fallback); + } return (T) mixed.get(key).get(state); } From a56329a5660d01cb78b50d1ff081401a7e9413fc Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 17:20:48 +0100 Subject: [PATCH 553/661] add class tests --- .../snackbag/mcvera/test/StyleTestApplication.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index fac219dd..0a77ae7d 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -1,6 +1,7 @@ package net.snackbag.mcvera.test; import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; @@ -44,13 +45,24 @@ public void init() { testRect.setStyle("transition", 100); testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); testRect.setStyle("background-color", StyleState.CLICKED, VColor.MC_RED); + + // Moving & classes + VLabel testLabel = new VLabel("hello there", 40, 10, this) + .alsoAddClass("label") + .alsoAdd(); + + testLabel.onMouseDragLeft((ctx) -> testLabel.move( + testLabel.getX() + ctx.moveX(), + testLabel.getY() + ctx.moveY() + )); } public VStyleSheet createStyleSheet() { VStyleSheet sheet = new VStyleSheet(); - sheet.setKey("label", "font", VFont.create()); + sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD.sub(80))); sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD), StyleState.HOVERED); + sheet.setKey("label", "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); return sheet; } From 07d05719a51f843ec2f16d0fe32bae0809e275ff Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 17:21:08 +0100 Subject: [PATCH 554/661] move test rect to fit the label --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 0a77ae7d..2a2d899e 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -33,11 +33,9 @@ public void init() { mergeStyleSheet(createStyleSheet()); - new VLabel("helo", this) - .alsoAddClass("label") - .alsoAdd(); - + // Animations VRect testRect = new VRect(VColor.black(), this).alsoAdd(); + testRect.move(10); testRect.onLeftClick(() -> testRect.animations.startOrRewind(longTestAnimation)); testRect.onRightClick(() -> testRect.animations.unwind(longTestAnimation)); From 5823515c2fbb54b4f516e331681772e888c7848c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 18:37:30 +0100 Subject: [PATCH 555/661] fix point not working for moved apps --- src/main/java/net/snackbag/vera/core/VeraApp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 187ee453..f26cd401 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -266,8 +266,8 @@ public List getShortcuts() { private boolean isPointOverWidget(VWidget widget, int px, int py) { if (!widget.visibilityConditionsPassed()) return false; - int widgetX = widget.getHitboxX() + x; - int widgetY = widget.getHitboxY() + y; + int widgetX = widget.getHitboxX(); + int widgetY = widget.getHitboxY(); int widgetWidth = widget.getHitboxWidth(); int widgetHeight = widget.getHitboxHeight(); return VGeometry.isInBox(px, py, widgetX, widgetY, widgetWidth, widgetHeight); From af9a7e04de1c116530c9b95fc978f23d877adc14 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 19:09:09 +0100 Subject: [PATCH 556/661] fi hierarchy doubles --- src/main/java/net/snackbag/vera/core/VeraApp.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index f26cd401..d08a5a67 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -171,8 +171,9 @@ public int getY() { } public void setRequiresHierarchy(boolean requires) { - if (MCVeraData.appHierarchy.contains(this) && !requires) { - MCVeraData.appHierarchy.remove(this); + if (MCVeraData.appHierarchy.contains(this)) { + if (!requires) MCVeraData.appHierarchy.remove(this); + return; } MCVeraData.appHierarchy.add(this); From 54e5777a233a100ad284eacff7e349e037519533 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 19:09:31 +0100 Subject: [PATCH 557/661] add wanring message --- src/main/java/net/snackbag/vera/core/VeraApp.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index d08a5a67..6a5a9c25 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -2,6 +2,7 @@ import net.minecraft.client.MinecraftClient; import net.snackbag.mcvera.MCVeraData; +import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.Vera; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; @@ -181,7 +182,10 @@ public void setRequiresHierarchy(boolean requires) { } public void moveToHierarchyTop() { - if (!requiresHierarchy) return; + if (!requiresHierarchy) { + MinecraftVera.LOGGER.warn("Failed to move app to top, because hierarchy isn't enabled"); + return; + } MCVeraData.appHierarchy.remove(this); MCVeraData.appHierarchy.add(0, this); From f33e1d609a7ac011d54983e422f307b1fb87ff4f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 19:09:54 +0100 Subject: [PATCH 558/661] add scheduling tasks to next frame --- .../snackbag/mcvera/mixin/GameRendererMixin.java | 9 ++++++++- .../java/net/snackbag/vera/InternalVera.java | 16 ++++++++++++++++ src/main/java/net/snackbag/vera/Vera.java | 9 +++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/snackbag/vera/InternalVera.java diff --git a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java index e86e3e00..754b51c4 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java @@ -1,6 +1,7 @@ package net.snackbag.mcvera.mixin; import net.minecraft.client.render.GameRenderer; +import net.snackbag.vera.InternalVera; import net.snackbag.vera.Vera; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.spongepowered.asm.mixin.Mixin; @@ -8,7 +9,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.LinkedHashSet; +import java.util.ArrayList; +import java.util.List; @Mixin(GameRenderer.class) public abstract class GameRendererMixin { @@ -31,5 +33,10 @@ public abstract class GameRendererMixin { private void mcvera$renderScreenAndTop(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { Vera.renderer.renderApps(VWindowPositioningFlag.SCREEN); Vera.renderer.renderApps(VWindowPositioningFlag.TOP); + + List tasks = new ArrayList<>(InternalVera.getScheduledTasks()); + InternalVera.clearScheduledTasks(); + + for (Runnable task : tasks) task.run(); } } diff --git a/src/main/java/net/snackbag/vera/InternalVera.java b/src/main/java/net/snackbag/vera/InternalVera.java new file mode 100644 index 00000000..cee052e7 --- /dev/null +++ b/src/main/java/net/snackbag/vera/InternalVera.java @@ -0,0 +1,16 @@ +package net.snackbag.vera; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; + +@ApiStatus.Internal +public class InternalVera { + public static List getScheduledTasks() { + return Vera.nextFrameTasks; + } + + public static void clearScheduledTasks() { + Vera.nextFrameTasks.clear(); + } +} diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index 58136e15..c8357f73 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -27,6 +27,7 @@ public class Vera { public static final String FONT_ARIAL = "minecraft:arial"; public static long renderCacheId = 0; + protected static final ArrayList nextFrameTasks = new ArrayList<>(); public static void forVisibleAndAllowedApps(Consumer handler) { final List handledApps = new ArrayList<>(); @@ -102,4 +103,12 @@ public static boolean isTopHierarchy(VeraApp app) { return null; } + + /** + * Schedules a task to the next frame; run AFTER all Vera rendering + * @param runnable the task to execute + */ + public static void scheduleToNextFrame(Runnable runnable) { + nextFrameTasks.add(runnable); + } } From e22ecaa800f614e3d457021fd33865bb5c026808 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 19:10:51 +0100 Subject: [PATCH 559/661] add hierarchy tests --- .../net/snackbag/mcvera/InternalCommands.java | 50 ++++++++--------- .../java/net/snackbag/mcvera/MCVeraData.java | 4 ++ .../snackbag/mcvera/impl/MCVeraRenderer.java | 5 +- .../snackbag/mcvera/test/HierarchyTest.java | 53 +++++++++++++++++++ 4 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 src/main/java/net/snackbag/mcvera/test/HierarchyTest.java diff --git a/src/main/java/net/snackbag/mcvera/InternalCommands.java b/src/main/java/net/snackbag/mcvera/InternalCommands.java index 1e49e148..83346ce6 100644 --- a/src/main/java/net/snackbag/mcvera/InternalCommands.java +++ b/src/main/java/net/snackbag/mcvera/InternalCommands.java @@ -6,10 +6,7 @@ import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.command.CommandRegistryAccess; -import net.snackbag.mcvera.test.LayoutCenteringTestApplication; -import net.snackbag.mcvera.test.LayoutTestApplication; -import net.snackbag.mcvera.test.StyleTestApplication; -import net.snackbag.mcvera.test.TestApplication; +import net.snackbag.mcvera.test.*; public class InternalCommands { public static void register( @@ -21,26 +18,30 @@ public static void register( dispatcher.register( ClientCommandManager.literal("vera") .then(ClientCommandManager.literal("test") - .then(ClientCommandManager.literal("generic").executes((ctx) -> { - TestApplication.INSTANCE.show(); - return 1; - })) - .then(ClientCommandManager.literal("styles").executes((ctx) -> { - StyleTestApplication.INSTANCE.show(); - return 1; - })) - .then(ClientCommandManager.literal("layout").executes((ctx) -> { - LayoutTestApplication.INSTANCE.show(); - return 1; - })) - .then(ClientCommandManager.literal("layoutalignments").executes((ctx) -> { - LayoutCenteringTestApplication.INSTANCE.show(); - return 1; - })) - .then(ClientCommandManager.literal("demo").executes(ctx -> { - DemoMod.init(); - return 1; - })) + .then(ClientCommandManager.literal("generic").executes((ctx) -> { + TestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("styles").executes((ctx) -> { + StyleTestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("layout").executes((ctx) -> { + LayoutTestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("layoutalignments").executes((ctx) -> { + LayoutCenteringTestApplication.INSTANCE.show(); + return 1; + })) + .then(ClientCommandManager.literal("hierarchy").executes(ctx -> { + HierarchyTest.INSTANCE.start(); + return 1; + })) + .then(ClientCommandManager.literal("demo").executes(ctx -> { + DemoMod.init(); + return 1; + })) ) .then(ClientCommandManager.literal("clear-tests") .executes((ctx) -> { @@ -48,6 +49,7 @@ public static void register( StyleTestApplication.INSTANCE = new StyleTestApplication(); LayoutTestApplication.INSTANCE = new LayoutTestApplication(); LayoutCenteringTestApplication.INSTANCE = new LayoutCenteringTestApplication(); + HierarchyTest.INSTANCE = new HierarchyTest(); return 1; }) ) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index f75b54c2..2c4e8a72 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -34,4 +34,8 @@ public static boolean asTopHierarchy(@NotNull Consumer runnable) { public static @Nullable VeraApp getTopHierarchy() { return appHierarchy.isEmpty() ? null : appHierarchy.get(0); } + + public static boolean isTopHierarchy(VeraApp app) { + return appHierarchy.isEmpty() || appHierarchy.get(0) == app; + } } diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index be4acadf..7f38d13f 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -69,7 +69,10 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.enableBlend(); app.render(); - VWidget hoveredWidget = app.getTopWidgetAt(Vera.getMouseX(), Vera.getMouseY()); + VWidget hoveredWidget = !MCVeraData.appHierarchy.contains(app) || MCVeraData.isTopHierarchy(app) + ? app.getTopWidgetAt(Vera.getMouseX(), Vera.getMouseY()) + : null; + for (VWidget widget : widgets) { if (widget != hoveredWidget && widget.isHovered()) widget.setHovered(false); else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java new file mode 100644 index 00000000..68ea09ba --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -0,0 +1,53 @@ +package net.snackbag.mcvera.test; + +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFont; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.widget.VLabel; + +public class HierarchyTest { + public static HierarchyTest INSTANCE = new HierarchyTest(); + + public void start() { + Application first = new Application("first"); + Application second = new Application("second"); + Application third = new Application("third"); + + first.show(); + first.move(10); + + second.show(); + second.move(120, 10); + + third.show(); + third.move(230, 10); + + Vera.scheduleToNextFrame(first::moveToHierarchyTop); + } + + public static class Application extends VeraApp { + private final String name; + + public Application(String name) { + this.name = name; + } + + @Override + public void init() { + setBackgroundColor(VColor.MC_DARK_GRAY); + setRequiresHierarchy(true); + + styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white())); + styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA), StyleState.HOVERED); + styleSheet.setKey(VLabel.class, "transition", 250); + new VLabel(name, this).alsoAdd(); + } + + @Override + public void update() { + setSize(100, 200); + } + } +} From f89f331d80faa77ed37237d5341d5d803d8cb285 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 22:25:11 +0100 Subject: [PATCH 560/661] add move thing for hierarchic app --- src/main/java/net/snackbag/mcvera/test/HierarchyTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java index 68ea09ba..d2fe79df 100644 --- a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -6,6 +6,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.VLabel; +import net.snackbag.vera.widget.VRect; public class HierarchyTest { public static HierarchyTest INSTANCE = new HierarchyTest(); @@ -42,6 +43,9 @@ public void init() { styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white())); styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA), StyleState.HOVERED); styleSheet.setKey(VLabel.class, "transition", 250); + + VRect mover = new VRect(VColor.black(), 0, 0, 100, 8, this).alsoAdd(); + mover.onMouseDragLeft((ctx) -> move(getX() + ctx.moveX(), getY() + ctx.moveY())); new VLabel(name, this).alsoAdd(); } From 77e9c102af6cfc5315f499ef517fabfea81918f1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 22:25:34 +0100 Subject: [PATCH 561/661] add visual indicator for hierarchy --- .../java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 ++ src/main/java/net/snackbag/vera/core/VeraApp.java | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 7f38d13f..93154d89 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -90,6 +90,8 @@ public void renderApp(VeraApp app) { } app.renderAfterWidgets(); + if (app.isRequiresHierarchy() && !MCVeraData.isTopHierarchy(app)) app.renderHierarchyOverlay(); + if (!blendEnabled) RenderSystem.disableBlend(); } diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 6a5a9c25..c1c8f82b 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -239,6 +239,14 @@ public void render() { public void renderAfterWidgets() {} + public void renderHierarchyOverlay() { + Vera.renderer.drawRect(this, 0, 0, width, height, 0, + backgroundColor.isTransparent() + ? VColor.black().withOpacity(0.2f) + : backgroundColor.sub(40).withOpacity(0.2f) + ); + } + public void update() {} public void addShortcut(VShortcut shortcut) { From 5d7196aa1a308de9ac009eb048da551f7d92f44c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 22:25:52 +0100 Subject: [PATCH 562/661] fix render order for hierarchic apps --- .../java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 93154d89..05b5dd4e 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -18,6 +18,7 @@ import org.lwjgl.opengl.GL11; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -99,18 +100,16 @@ public void renderApps(VWindowPositioningFlag flag) { if (!MinecraftClient.getInstance().isRunning()) return; LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); - List hierarchicApps = new ArrayList<>(); for (VeraApp app : apps) { - if (app.isRequiresHierarchy()) { - hierarchicApps.add(app); - continue; - } - + if (app.isRequiresHierarchy()) continue; Vera.renderer.renderApp(app); } + List hierarchicApps = new ArrayList<>(MCVeraData.appHierarchy); + Collections.reverse(hierarchicApps); for (VeraApp app : hierarchicApps) { + if (app.getPositioning() != flag) continue; Vera.renderer.renderApp(app); } } From d84a9429b03811c5d84093bf61e5f5ef5a0a2022 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 22:26:08 +0100 Subject: [PATCH 563/661] fix hierarchy app selection --- .../net/snackbag/mcvera/mixin/ParentElementMixin.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index a3fd8c3f..763c088a 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -15,6 +15,9 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.ArrayList; +import java.util.List; + @Mixin(ParentElement.class) public interface ParentElementMixin { @Inject(method = "mouseClicked", at = @At("HEAD")) @@ -25,10 +28,11 @@ public interface ParentElementMixin { VMouseButton btn = VMouseButton.fromInt(button); - VeraApp top = MCVeraData.getTopHierarchy(); + List hierarchicApps = new ArrayList<>(MCVeraData.appHierarchy); + for (VeraApp app : hierarchicApps) { + if (app.isPointOverThis(mouseX, mouseY)) { + if (MCVeraData.isTopHierarchy(app)) break; - for (VeraApp app : MCVeraData.appHierarchy) { - if (app.isPointOverThis(mouseX, mouseY) && top != app) { app.moveToHierarchyTop(); justChanged = true; break; From 5989403fcdec34a430bdcd5f63ea662de01c8ed0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 22:26:18 +0100 Subject: [PATCH 564/661] add more QOL constructors to VRect --- src/main/java/net/snackbag/vera/widget/VRect.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 935de528..d2e670b7 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -7,7 +7,15 @@ public class VRect extends VWidget { public VRect(VColor color, VeraApp app) { - super(0, 0, 20, 20, app); + this(color, 0, 0, 20, 20, app); + } + + public VRect(VColor color, int x, int y, VeraApp app) { + this(color, x, y, 20, 20, app); + } + + public VRect(VColor color, int x, int y, int width, int height, VeraApp app) { + super(x, y, width, height, app); this.focusOnClick = false; setStyle("background-color", color); From 5ed0f6be340fa2b09a7abfd720e704e4b060ffce Mon Sep 17 00:00:00 2001 From: JXSnack Date: Mon, 23 Feb 2026 22:30:06 +0100 Subject: [PATCH 565/661] add exit shortcut to hierarchy test --- src/main/java/net/snackbag/mcvera/test/HierarchyTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java index d2fe79df..63075c64 100644 --- a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -4,6 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -37,6 +38,8 @@ public Application(String name) { @Override public void init() { + new VShortcut(this, "escape", this::hide); + setBackgroundColor(VColor.MC_DARK_GRAY); setRequiresHierarchy(true); From b078dd0f566569bf90b7a8384c8d632dc088d79c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 11:31:19 +0100 Subject: [PATCH 566/661] fix hierarchic apps not respecting app visibility --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 05b5dd4e..01e6dc71 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -109,7 +109,9 @@ public void renderApps(VWindowPositioningFlag flag) { List hierarchicApps = new ArrayList<>(MCVeraData.appHierarchy); Collections.reverse(hierarchicApps); for (VeraApp app : hierarchicApps) { - if (app.getPositioning() != flag) continue; + if (app.getPositioning() != flag || !MCVeraData.visibleApplications.get(app.getPositioning()).contains(app)) { + continue; + } Vera.renderer.renderApp(app); } } From 4a7d37a1f3f48db69d3a2e158ca350f5af9d0f10 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 11:31:30 +0100 Subject: [PATCH 567/661] better looking test --- src/main/java/net/snackbag/mcvera/test/HierarchyTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java index 63075c64..30f15310 100644 --- a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -43,13 +43,15 @@ public void init() { setBackgroundColor(VColor.MC_DARK_GRAY); setRequiresHierarchy(true); - styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white())); - styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA), StyleState.HOVERED); + styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white()).withSize(12)); + styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA).withSize(12), StyleState.HOVERED); styleSheet.setKey(VLabel.class, "transition", 250); VRect mover = new VRect(VColor.black(), 0, 0, 100, 8, this).alsoAdd(); mover.onMouseDragLeft((ctx) -> move(getX() + ctx.moveX(), getY() + ctx.moveY())); - new VLabel(name, this).alsoAdd(); + + VLabel label = new VLabel(name, this).alsoAdd(); + label.move(1); } @Override From 0ac2178957fa34dd7fa391ed046dd327f74c2449 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 17:53:05 +0100 Subject: [PATCH 568/661] turn hierarchy system into flag system --- .../java/net/snackbag/mcvera/MCVeraData.java | 27 +++++++++++--- .../snackbag/mcvera/impl/MCVeraRenderer.java | 9 +++-- .../net/snackbag/mcvera/mixin/MouseMixin.java | 6 ++- .../mcvera/mixin/ParentElementMixin.java | 9 +++-- .../snackbag/mcvera/test/HierarchyTest.java | 3 +- src/main/java/net/snackbag/vera/Vera.java | 19 +++------- .../java/net/snackbag/vera/core/VeraApp.java | 37 ++++++++++--------- .../net/snackbag/vera/flag/VWindowFlag.java | 4 +- 8 files changed, 65 insertions(+), 49 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index 2c4e8a72..2faa2cf1 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -1,6 +1,7 @@ package net.snackbag.mcvera; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +12,7 @@ public class MCVeraData { public static LinkedHashSet applications = new LinkedHashSet<>(); public static HashMap> visibleApplications = new HashMap<>(); - public static List appHierarchy = new ArrayList<>(); + public static HashMap> windowFlags = new HashMap<>(); public static int appsWithMouseRequired = 0; @@ -25,17 +26,31 @@ public class MCVeraData { * @return whether something has been executed */ public static boolean asTopHierarchy(@NotNull Consumer runnable) { - if (appHierarchy.isEmpty()) return false; - - runnable.accept(appHierarchy.get(0)); + if (!windowFlags.containsKey(VWindowFlag.HIERARCHIC)) return false; + runnable.accept(getTopHierarchy()); return true; } public static @Nullable VeraApp getTopHierarchy() { - return appHierarchy.isEmpty() ? null : appHierarchy.get(0); + List apps = getAppsWithFlag(VWindowFlag.HIERARCHIC); + if (apps.isEmpty()) return null; + return apps.get(0); } public static boolean isTopHierarchy(VeraApp app) { - return appHierarchy.isEmpty() || appHierarchy.get(0) == app; + return getTopHierarchy() == app; + } + + /** + * Returns an UNMODIFIABLE version of the {@link #windowFlags} entry for the given flag. If the entry is empty, it + * returns an empty unmodifiable list. If you want to access a modifiable version of the flag, you have to manually + * work with the {@link #windowFlags} variable. + * + * @param flag the flag to check + * @return an unmodifiable list of the apps under the flag + */ + public static List getAppsWithFlag(VWindowFlag flag) { + if (!windowFlags.containsKey(flag)) return Collections.unmodifiableList(new ArrayList<>()); + return Collections.unmodifiableList(windowFlags.get(flag)); } } diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 01e6dc71..941d4642 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -13,6 +13,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.widget.VWidget; import org.lwjgl.opengl.GL11; @@ -70,7 +71,7 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.enableBlend(); app.render(); - VWidget hoveredWidget = !MCVeraData.appHierarchy.contains(app) || MCVeraData.isTopHierarchy(app) + VWidget hoveredWidget = !app.hasFlag(VWindowFlag.HIERARCHIC) || MCVeraData.isTopHierarchy(app) ? app.getTopWidgetAt(Vera.getMouseX(), Vera.getMouseY()) : null; @@ -91,7 +92,7 @@ public void renderApp(VeraApp app) { } app.renderAfterWidgets(); - if (app.isRequiresHierarchy() && !MCVeraData.isTopHierarchy(app)) app.renderHierarchyOverlay(); + if (app.hasFlag(VWindowFlag.HIERARCHIC) && !MCVeraData.isTopHierarchy(app)) app.renderHierarchyOverlay(); if (!blendEnabled) RenderSystem.disableBlend(); } @@ -102,11 +103,11 @@ public void renderApps(VWindowPositioningFlag flag) { LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); for (VeraApp app : apps) { - if (app.isRequiresHierarchy()) continue; + if (app.hasFlag(VWindowFlag.HIERARCHIC)) continue; Vera.renderer.renderApp(app); } - List hierarchicApps = new ArrayList<>(MCVeraData.appHierarchy); + List hierarchicApps = new ArrayList<>(MCVeraData.getAppsWithFlag(VWindowFlag.HIERARCHIC)); Collections.reverse(hierarchicApps); for (VeraApp app : hierarchicApps) { if (app.getPositioning() != flag || !MCVeraData.visibleApplications.get(app.getPositioning()).contains(app)) { diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index 199fb5a8..60634b61 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -2,10 +2,12 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.Mouse; +import net.snackbag.mcvera.MCVeraData; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; @@ -31,9 +33,9 @@ public abstract class MouseMixin { int mouseX = (int) (fx / scaleFactor); int mouseY = (int) (fy / scaleFactor); - VeraApp top = Vera.getTopHierarchyApp(); + VeraApp top = MCVeraData.getTopHierarchy(); Vera.forAllVisibleApps(app -> { - if (app.isRequiresHierarchy() && app != top) return; + if (app.hasFlag(VWindowFlag.HIERARCHIC) && app != top) return; VWidget widget = app.getTopWidgetAt(mouseX, mouseY); if (widget != null) widget.events.fire(VEvents.Widget.MOUSE_MOVE, mouseX, mouseY); diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index 763c088a..bcac3019 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -6,6 +6,7 @@ import net.snackbag.vera.core.VMouseButton; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -28,7 +29,7 @@ public interface ParentElementMixin { VMouseButton btn = VMouseButton.fromInt(button); - List hierarchicApps = new ArrayList<>(MCVeraData.appHierarchy); + List hierarchicApps = new ArrayList<>(MCVeraData.getAppsWithFlag(VWindowFlag.HIERARCHIC)); for (VeraApp app : hierarchicApps) { if (app.isPointOverThis(mouseX, mouseY)) { if (MCVeraData.isTopHierarchy(app)) break; @@ -48,7 +49,7 @@ public interface ParentElementMixin { }); Vera.forAllVisibleApps(app -> { - if (app.isRequiresHierarchy()) return; + if (app.hasFlag(VWindowFlag.HIERARCHIC)) return; VWidget hoveredWidget = app.getTopWidgetAt(mouseX, mouseY); if (hoveredWidget != null) handleClickEvents(hoveredWidget, btn); @@ -78,7 +79,7 @@ private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), btn)); Vera.forAllVisibleApps(app -> { - if (app.isRequiresHierarchy()) return; + if (app.hasFlag(VWindowFlag.HIERARCHIC)) return; if (!app.isPointOverThis(mouseX, mouseY)) return; handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), btn); @@ -105,7 +106,7 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount)); Vera.forAllVisibleApps(app -> { - if (app.isRequiresHierarchy()) return; + if (app.hasFlag(VWindowFlag.HIERARCHIC)) return; if (!app.isPointOverThis(mouseX, mouseY)) return; handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount); diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java index 30f15310..cfbca858 100644 --- a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -5,6 +5,7 @@ import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -41,7 +42,7 @@ public void init() { new VShortcut(this, "escape", this::hide); setBackgroundColor(VColor.MC_DARK_GRAY); - setRequiresHierarchy(true); + setFlag(VWindowFlag.HIERARCHIC, true); styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white()).withSize(12)); styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA).withSize(12), StyleState.HOVERED); diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index c8357f73..27fb85cf 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -6,6 +6,7 @@ import net.snackbag.mcvera.impl.MCVeraRegistrar; import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; @@ -31,16 +32,16 @@ public class Vera { public static void forVisibleAndAllowedApps(Consumer handler) { final List handledApps = new ArrayList<>(); - if (!MCVeraData.appHierarchy.isEmpty()) { - VeraApp app = MCVeraData.appHierarchy.get(0); - handledApps.add(app); - handler.accept(app); + VeraApp topHierarchy = MCVeraData.getTopHierarchy(); + if (topHierarchy != null) { + handledApps.add(topHierarchy); + handler.accept(topHierarchy); } for (VWindowPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { for (VeraApp app : MCVeraData.visibleApplications.get(flag)) { - if (handledApps.contains(app) || app.isRequiresHierarchy()) continue; + if (handledApps.contains(app) || app.hasFlag(VWindowFlag.HIERARCHIC)) continue; handler.accept(app); handledApps.add(app); @@ -86,14 +87,6 @@ public static int getMouseY() { } } - public static @Nullable VeraApp getTopHierarchyApp() { - return MCVeraData.appHierarchy.isEmpty() ? null : MCVeraData.appHierarchy.get(0); - } - - public static boolean isTopHierarchy(VeraApp app) { - return getTopHierarchyApp() == app; - } - @SafeVarargs public static @Nullable T firstOf(Predicate evaluator, T... values) { for (T v : values) { diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index c1c8f82b..b1f9930d 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -6,6 +6,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.util.VGeometry; @@ -31,7 +32,6 @@ public abstract class VeraApp { private int height; private boolean visible; - private boolean requiresHierarchy; private @Nullable VWidget focusedWidget; private VWindowPositioningFlag positioning; @@ -171,28 +171,14 @@ public int getY() { return y; } - public void setRequiresHierarchy(boolean requires) { - if (MCVeraData.appHierarchy.contains(this)) { - if (!requires) MCVeraData.appHierarchy.remove(this); - return; - } - - MCVeraData.appHierarchy.add(this); - this.requiresHierarchy = requires; - } - public void moveToHierarchyTop() { - if (!requiresHierarchy) { + if (!hasFlag(VWindowFlag.HIERARCHIC)) { MinecraftVera.LOGGER.warn("Failed to move app to top, because hierarchy isn't enabled"); return; } - MCVeraData.appHierarchy.remove(this); - MCVeraData.appHierarchy.add(0, this); - } - - public boolean isRequiresHierarchy() { - return requiresHierarchy; + MCVeraData.windowFlags.get(VWindowFlag.HIERARCHIC).remove(this); + MCVeraData.windowFlags.get(VWindowFlag.HIERARCHIC).add(0, this); } public abstract void init(); @@ -360,4 +346,19 @@ public void charTyped(char chr, int modifiers) { public void mergeStyleSheet(VStyleSheet target) { styleSheet.addSheet(target); } + + public boolean hasFlag(VWindowFlag flag) { + if (!MCVeraData.windowFlags.containsKey(flag)) return false; + else return MCVeraData.windowFlags.get(flag).contains(this); + } + + public void setFlag(VWindowFlag flag, boolean enabled) { + if (enabled == hasFlag(flag)) return; // if nothing has to be changed, change nothing + + if (!enabled) MCVeraData.windowFlags.get(flag).remove(this); + else { + if (!MCVeraData.windowFlags.containsKey(flag)) MCVeraData.windowFlags.put(flag, new ArrayList<>()); + MCVeraData.windowFlags.get(flag).add(this); + } + } } diff --git a/src/main/java/net/snackbag/vera/flag/VWindowFlag.java b/src/main/java/net/snackbag/vera/flag/VWindowFlag.java index 3ab843d8..ae0015b3 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VWindowFlag.java @@ -1,5 +1,7 @@ package net.snackbag.vera.flag; public enum VWindowFlag { - DEBUG + DEBUG, + HIERARCHIC, + REQUIRES_MOUSE } From b95ef734a5b61133a8d5fb9664e51f4e993f9a40 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 18:11:35 +0100 Subject: [PATCH 569/661] move most mouse requirement tracking to window flag system --- .../snackbag/mcvera/impl/MCVeraProvider.java | 5 +-- .../snackbag/mcvera/test/TestApplication.java | 9 +++-- .../java/net/snackbag/vera/core/VeraApp.java | 35 ++++++++----------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index a27e35ef..2df5b476 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -8,6 +8,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.widget.VWidget; import java.nio.file.Path; @@ -26,7 +27,7 @@ public void handleAppShow(VeraApp app) { if (app.isVisible()) return; MCVeraData.visibleApplications.get(app.getPositioning()).add(app); - if (app.isMouseRequired()) MCVeraData.appsWithMouseRequired += 1; + if (app.hasFlag(VWindowFlag.REQUIRES_MOUSE)) MCVeraData.appsWithMouseRequired += 1; MinecraftClient client = MinecraftClient.getInstance(); client.send(app::update); @@ -40,7 +41,7 @@ public void handleAppShow(VeraApp app) { public void handleAppHide(VeraApp app) { if (!app.isVisible()) return; - if (app.isMouseRequired()) MCVeraData.appsWithMouseRequired -= 1; + if (app.hasFlag(VWindowFlag.REQUIRES_MOUSE)) MCVeraData.appsWithMouseRequired -= 1; MCVeraData.visibleApplications.get(app.getPositioning()).remove(app); MinecraftClient client = MinecraftClient.getInstance(); client.send(app::update); diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 80c84255..12e30ec7 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -8,6 +8,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; +import net.snackbag.vera.flag.VWindowFlag; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.*; @@ -31,9 +32,7 @@ public void init() { this.hide(); }); - new VShortcut(this, "leftalt+m", () -> { - setMouseRequired(!isMouseRequired()); - }); + new VShortcut(this, "leftalt+m", () -> toggleFlag(VWindowFlag.REQUIRES_MOUSE)); VLineInput input = new VLineInput(this).alsoAdd(); input.setMaxChars(15); @@ -124,6 +123,10 @@ public void init() { rotationRect.move(20, 200); } + private void toggleFlag(VWindowFlag flag) { + setFlag(flag, !hasFlag(flag)); + } + @Override public void update() { super.update(); diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index b1f9930d..509606f0 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -24,7 +24,6 @@ public abstract class VeraApp { private VColor backgroundColor; private VCursorShape cursorShape; private boolean cursorVisible; - private boolean mouseRequired; private int x; private int y; @@ -45,7 +44,7 @@ public VeraApp(boolean mouseRequired) { this.backgroundColor = VColor.transparent(); this.cursorShape = VCursorShape.DEFAULT; this.cursorVisible = true; - this.mouseRequired = mouseRequired; + if (mouseRequired) setFlag(VWindowFlag.REQUIRES_MOUSE, true); Vera.provider.handleAppInitialization(this); @@ -61,7 +60,7 @@ public VeraApp(boolean mouseRequired) { public void setCursorVisible(boolean cursorVisible) { this.cursorVisible = cursorVisible; - if (!visible || !mouseRequired) return; + if (!visible || !hasFlag(VWindowFlag.REQUIRES_MOUSE)) return; GLFW.glfwSetInputMode( MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, @@ -80,23 +79,6 @@ public boolean isCursorVisible() { return cursorVisible; } - public boolean isMouseRequired() { - return mouseRequired; - } - - public void setMouseRequired(boolean mouseRequired) { - if (this.mouseRequired == mouseRequired) return; - - Vera.provider.handleAppSetMouseRequired(this, mouseRequired); - this.mouseRequired = mouseRequired; - - if (!visible || !mouseRequired) return; - GLFW.glfwSetInputMode( - MinecraftClient.getInstance().getWindow().getHandle(), - GLFW.GLFW_CURSOR, - cursorVisible ? GLFW.GLFW_CURSOR_NORMAL : GLFW.GLFW_CURSOR_HIDDEN); - } - public boolean isVisible() { return visible; } @@ -122,7 +104,7 @@ public void setVisibility(boolean visible) { this.visible = visible; if (visible) setCursorShape(cursorShape); - if (!visible || !mouseRequired) return; + if (!visible || !hasFlag(VWindowFlag.REQUIRES_MOUSE)) return; GLFW.glfwSetInputMode( MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, @@ -360,5 +342,16 @@ public void setFlag(VWindowFlag flag, boolean enabled) { if (!MCVeraData.windowFlags.containsKey(flag)) MCVeraData.windowFlags.put(flag, new ArrayList<>()); MCVeraData.windowFlags.get(flag).add(this); } + + // handle mouse requirements + if (flag == VWindowFlag.REQUIRES_MOUSE) { + Vera.provider.handleAppSetMouseRequired(this, enabled); + + if (!visible || !enabled) return; + GLFW.glfwSetInputMode( + MinecraftClient.getInstance().getWindow().getHandle(), + GLFW.GLFW_CURSOR, + cursorVisible ? GLFW.GLFW_CURSOR_NORMAL : GLFW.GLFW_CURSOR_HIDDEN); + } } } From 273a0fd0b58fac2243d79e36def2341fe00e7c6a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 18:12:54 +0100 Subject: [PATCH 570/661] VWindowFlag -> VAppFlag --- .../java/net/snackbag/mcvera/MCVeraData.java | 18 +++++------ .../snackbag/mcvera/impl/MCVeraProvider.java | 6 ++-- .../snackbag/mcvera/impl/MCVeraRenderer.java | 10 +++---- .../net/snackbag/mcvera/mixin/MouseMixin.java | 4 +-- .../mcvera/mixin/ParentElementMixin.java | 10 +++---- .../snackbag/mcvera/test/HierarchyTest.java | 4 +-- .../snackbag/mcvera/test/TestApplication.java | 6 ++-- src/main/java/net/snackbag/vera/Vera.java | 4 +-- .../java/net/snackbag/vera/core/VeraApp.java | 30 +++++++++---------- .../flag/{VWindowFlag.java => VAppFlag.java} | 2 +- 10 files changed, 47 insertions(+), 47 deletions(-) rename src/main/java/net/snackbag/vera/flag/{VWindowFlag.java => VAppFlag.java} (75%) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index 2faa2cf1..6af78075 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -1,7 +1,7 @@ package net.snackbag.mcvera; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,7 +12,7 @@ public class MCVeraData { public static LinkedHashSet applications = new LinkedHashSet<>(); public static HashMap> visibleApplications = new HashMap<>(); - public static HashMap> windowFlags = new HashMap<>(); + public static HashMap> appFlags = new HashMap<>(); public static int appsWithMouseRequired = 0; @@ -26,13 +26,13 @@ public class MCVeraData { * @return whether something has been executed */ public static boolean asTopHierarchy(@NotNull Consumer runnable) { - if (!windowFlags.containsKey(VWindowFlag.HIERARCHIC)) return false; + if (!appFlags.containsKey(VAppFlag.HIERARCHIC)) return false; runnable.accept(getTopHierarchy()); return true; } public static @Nullable VeraApp getTopHierarchy() { - List apps = getAppsWithFlag(VWindowFlag.HIERARCHIC); + List apps = getAppsWithFlag(VAppFlag.HIERARCHIC); if (apps.isEmpty()) return null; return apps.get(0); } @@ -42,15 +42,15 @@ public static boolean isTopHierarchy(VeraApp app) { } /** - * Returns an UNMODIFIABLE version of the {@link #windowFlags} entry for the given flag. If the entry is empty, it + * Returns an UNMODIFIABLE version of the {@link #appFlags} entry for the given flag. If the entry is empty, it * returns an empty unmodifiable list. If you want to access a modifiable version of the flag, you have to manually - * work with the {@link #windowFlags} variable. + * work with the {@link #appFlags} variable. * * @param flag the flag to check * @return an unmodifiable list of the apps under the flag */ - public static List getAppsWithFlag(VWindowFlag flag) { - if (!windowFlags.containsKey(flag)) return Collections.unmodifiableList(new ArrayList<>()); - return Collections.unmodifiableList(windowFlags.get(flag)); + public static List getAppsWithFlag(VAppFlag flag) { + if (!appFlags.containsKey(flag)) return Collections.unmodifiableList(new ArrayList<>()); + return Collections.unmodifiableList(appFlags.get(flag)); } } diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java index 2df5b476..f7af9299 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraProvider.java @@ -8,7 +8,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.widget.VWidget; import java.nio.file.Path; @@ -27,7 +27,7 @@ public void handleAppShow(VeraApp app) { if (app.isVisible()) return; MCVeraData.visibleApplications.get(app.getPositioning()).add(app); - if (app.hasFlag(VWindowFlag.REQUIRES_MOUSE)) MCVeraData.appsWithMouseRequired += 1; + if (app.hasFlag(VAppFlag.REQUIRES_MOUSE)) MCVeraData.appsWithMouseRequired += 1; MinecraftClient client = MinecraftClient.getInstance(); client.send(app::update); @@ -41,7 +41,7 @@ public void handleAppShow(VeraApp app) { public void handleAppHide(VeraApp app) { if (!app.isVisible()) return; - if (app.hasFlag(VWindowFlag.REQUIRES_MOUSE)) MCVeraData.appsWithMouseRequired -= 1; + if (app.hasFlag(VAppFlag.REQUIRES_MOUSE)) MCVeraData.appsWithMouseRequired -= 1; MCVeraData.visibleApplications.get(app.getPositioning()).remove(app); MinecraftClient client = MinecraftClient.getInstance(); client.send(app::update); diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 941d4642..8e897dd2 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -13,7 +13,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.widget.VWidget; import org.lwjgl.opengl.GL11; @@ -71,7 +71,7 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.enableBlend(); app.render(); - VWidget hoveredWidget = !app.hasFlag(VWindowFlag.HIERARCHIC) || MCVeraData.isTopHierarchy(app) + VWidget hoveredWidget = !app.hasFlag(VAppFlag.HIERARCHIC) || MCVeraData.isTopHierarchy(app) ? app.getTopWidgetAt(Vera.getMouseX(), Vera.getMouseY()) : null; @@ -92,7 +92,7 @@ public void renderApp(VeraApp app) { } app.renderAfterWidgets(); - if (app.hasFlag(VWindowFlag.HIERARCHIC) && !MCVeraData.isTopHierarchy(app)) app.renderHierarchyOverlay(); + if (app.hasFlag(VAppFlag.HIERARCHIC) && !MCVeraData.isTopHierarchy(app)) app.renderHierarchyOverlay(); if (!blendEnabled) RenderSystem.disableBlend(); } @@ -103,11 +103,11 @@ public void renderApps(VWindowPositioningFlag flag) { LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); for (VeraApp app : apps) { - if (app.hasFlag(VWindowFlag.HIERARCHIC)) continue; + if (app.hasFlag(VAppFlag.HIERARCHIC)) continue; Vera.renderer.renderApp(app); } - List hierarchicApps = new ArrayList<>(MCVeraData.getAppsWithFlag(VWindowFlag.HIERARCHIC)); + List hierarchicApps = new ArrayList<>(MCVeraData.getAppsWithFlag(VAppFlag.HIERARCHIC)); Collections.reverse(hierarchicApps); for (VeraApp app : hierarchicApps) { if (app.getPositioning() != flag || !MCVeraData.visibleApplications.get(app.getPositioning()).contains(app)) { diff --git a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java index 60634b61..cbe1c15f 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/MouseMixin.java @@ -7,7 +7,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.spongepowered.asm.mixin.Final; @@ -35,7 +35,7 @@ public abstract class MouseMixin { VeraApp top = MCVeraData.getTopHierarchy(); Vera.forAllVisibleApps(app -> { - if (app.hasFlag(VWindowFlag.HIERARCHIC) && app != top) return; + if (app.hasFlag(VAppFlag.HIERARCHIC) && app != top) return; VWidget widget = app.getTopWidgetAt(mouseX, mouseY); if (widget != null) widget.events.fire(VEvents.Widget.MOUSE_MOVE, mouseX, mouseY); diff --git a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java index bcac3019..75bf473a 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/ParentElementMixin.java @@ -6,7 +6,7 @@ import net.snackbag.vera.core.VMouseButton; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.util.DragHandler; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -29,7 +29,7 @@ public interface ParentElementMixin { VMouseButton btn = VMouseButton.fromInt(button); - List hierarchicApps = new ArrayList<>(MCVeraData.getAppsWithFlag(VWindowFlag.HIERARCHIC)); + List hierarchicApps = new ArrayList<>(MCVeraData.getAppsWithFlag(VAppFlag.HIERARCHIC)); for (VeraApp app : hierarchicApps) { if (app.isPointOverThis(mouseX, mouseY)) { if (MCVeraData.isTopHierarchy(app)) break; @@ -49,7 +49,7 @@ public interface ParentElementMixin { }); Vera.forAllVisibleApps(app -> { - if (app.hasFlag(VWindowFlag.HIERARCHIC)) return; + if (app.hasFlag(VAppFlag.HIERARCHIC)) return; VWidget hoveredWidget = app.getTopWidgetAt(mouseX, mouseY); if (hoveredWidget != null) handleClickEvents(hoveredWidget, btn); @@ -79,7 +79,7 @@ private void handleClickEvents(@Nullable VWidget widget, VMouseButton button) MCVeraData.asTopHierarchy(app -> handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), btn)); Vera.forAllVisibleApps(app -> { - if (app.hasFlag(VWindowFlag.HIERARCHIC)) return; + if (app.hasFlag(VAppFlag.HIERARCHIC)) return; if (!app.isPointOverThis(mouseX, mouseY)) return; handleReleaseEvents(app.getTopWidgetAt(mouseX, mouseY), btn); @@ -106,7 +106,7 @@ private void handleReleaseEvents(@Nullable VWidget widget, VMouseButton butto MCVeraData.asTopHierarchy(app -> handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount)); Vera.forAllVisibleApps(app -> { - if (app.hasFlag(VWindowFlag.HIERARCHIC)) return; + if (app.hasFlag(VAppFlag.HIERARCHIC)) return; if (!app.isPointOverThis(mouseX, mouseY)) return; handleScrollEvents(app.getTopWidgetAt(mouseX, mouseY), mouseX, mouseY, amount); diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java index cfbca858..4e78c344 100644 --- a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -5,7 +5,7 @@ import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -42,7 +42,7 @@ public void init() { new VShortcut(this, "escape", this::hide); setBackgroundColor(VColor.MC_DARK_GRAY); - setFlag(VWindowFlag.HIERARCHIC, true); + setFlag(VAppFlag.HIERARCHIC, true); styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white()).withSize(12)); styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA).withSize(12), StyleState.HOVERED); diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 12e30ec7..4ae6f406 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -8,7 +8,7 @@ import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.style.StyleState; import net.snackbag.vera.widget.*; @@ -32,7 +32,7 @@ public void init() { this.hide(); }); - new VShortcut(this, "leftalt+m", () -> toggleFlag(VWindowFlag.REQUIRES_MOUSE)); + new VShortcut(this, "leftalt+m", () -> toggleFlag(VAppFlag.REQUIRES_MOUSE)); VLineInput input = new VLineInput(this).alsoAdd(); input.setMaxChars(15); @@ -123,7 +123,7 @@ public void init() { rotationRect.move(20, 200); } - private void toggleFlag(VWindowFlag flag) { + private void toggleFlag(VAppFlag flag) { setFlag(flag, !hasFlag(flag)); } diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index 27fb85cf..a411d3ba 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -6,7 +6,7 @@ import net.snackbag.mcvera.impl.MCVeraRegistrar; import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; @@ -41,7 +41,7 @@ public static void forVisibleAndAllowedApps(Consumer handler) { for (VWindowPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { for (VeraApp app : MCVeraData.visibleApplications.get(flag)) { - if (handledApps.contains(app) || app.hasFlag(VWindowFlag.HIERARCHIC)) continue; + if (handledApps.contains(app) || app.hasFlag(VAppFlag.HIERARCHIC)) continue; handler.accept(app); handledApps.add(app); diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 509606f0..be9152c7 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -6,7 +6,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; -import net.snackbag.vera.flag.VWindowFlag; +import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VWindowPositioningFlag; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.util.VGeometry; @@ -44,7 +44,7 @@ public VeraApp(boolean mouseRequired) { this.backgroundColor = VColor.transparent(); this.cursorShape = VCursorShape.DEFAULT; this.cursorVisible = true; - if (mouseRequired) setFlag(VWindowFlag.REQUIRES_MOUSE, true); + if (mouseRequired) setFlag(VAppFlag.REQUIRES_MOUSE, true); Vera.provider.handleAppInitialization(this); @@ -60,7 +60,7 @@ public VeraApp(boolean mouseRequired) { public void setCursorVisible(boolean cursorVisible) { this.cursorVisible = cursorVisible; - if (!visible || !hasFlag(VWindowFlag.REQUIRES_MOUSE)) return; + if (!visible || !hasFlag(VAppFlag.REQUIRES_MOUSE)) return; GLFW.glfwSetInputMode( MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, @@ -104,7 +104,7 @@ public void setVisibility(boolean visible) { this.visible = visible; if (visible) setCursorShape(cursorShape); - if (!visible || !hasFlag(VWindowFlag.REQUIRES_MOUSE)) return; + if (!visible || !hasFlag(VAppFlag.REQUIRES_MOUSE)) return; GLFW.glfwSetInputMode( MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, @@ -154,13 +154,13 @@ public int getY() { } public void moveToHierarchyTop() { - if (!hasFlag(VWindowFlag.HIERARCHIC)) { + if (!hasFlag(VAppFlag.HIERARCHIC)) { MinecraftVera.LOGGER.warn("Failed to move app to top, because hierarchy isn't enabled"); return; } - MCVeraData.windowFlags.get(VWindowFlag.HIERARCHIC).remove(this); - MCVeraData.windowFlags.get(VWindowFlag.HIERARCHIC).add(0, this); + MCVeraData.appFlags.get(VAppFlag.HIERARCHIC).remove(this); + MCVeraData.appFlags.get(VAppFlag.HIERARCHIC).add(0, this); } public abstract void init(); @@ -329,22 +329,22 @@ public void mergeStyleSheet(VStyleSheet target) { styleSheet.addSheet(target); } - public boolean hasFlag(VWindowFlag flag) { - if (!MCVeraData.windowFlags.containsKey(flag)) return false; - else return MCVeraData.windowFlags.get(flag).contains(this); + public boolean hasFlag(VAppFlag flag) { + if (!MCVeraData.appFlags.containsKey(flag)) return false; + else return MCVeraData.appFlags.get(flag).contains(this); } - public void setFlag(VWindowFlag flag, boolean enabled) { + public void setFlag(VAppFlag flag, boolean enabled) { if (enabled == hasFlag(flag)) return; // if nothing has to be changed, change nothing - if (!enabled) MCVeraData.windowFlags.get(flag).remove(this); + if (!enabled) MCVeraData.appFlags.get(flag).remove(this); else { - if (!MCVeraData.windowFlags.containsKey(flag)) MCVeraData.windowFlags.put(flag, new ArrayList<>()); - MCVeraData.windowFlags.get(flag).add(this); + if (!MCVeraData.appFlags.containsKey(flag)) MCVeraData.appFlags.put(flag, new ArrayList<>()); + MCVeraData.appFlags.get(flag).add(this); } // handle mouse requirements - if (flag == VWindowFlag.REQUIRES_MOUSE) { + if (flag == VAppFlag.REQUIRES_MOUSE) { Vera.provider.handleAppSetMouseRequired(this, enabled); if (!visible || !enabled) return; diff --git a/src/main/java/net/snackbag/vera/flag/VWindowFlag.java b/src/main/java/net/snackbag/vera/flag/VAppFlag.java similarity index 75% rename from src/main/java/net/snackbag/vera/flag/VWindowFlag.java rename to src/main/java/net/snackbag/vera/flag/VAppFlag.java index ae0015b3..25629499 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VAppFlag.java @@ -1,6 +1,6 @@ package net.snackbag.vera.flag; -public enum VWindowFlag { +public enum VAppFlag { DEBUG, HIERARCHIC, REQUIRES_MOUSE From d3a0e17a4dd0baa13345c2a46095e27848c8fe8a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 18:13:47 +0100 Subject: [PATCH 571/661] VWindowPositioningFlag -> VAppPositioningFlag --- src/main/java/net/snackbag/mcvera/MCVeraData.java | 4 ++-- .../net/snackbag/mcvera/impl/MCVeraRenderer.java | 4 ++-- .../net/snackbag/mcvera/mixin/GameRendererMixin.java | 12 ++++++------ .../net/snackbag/mcvera/mixin/InGameHudMixin.java | 10 +++++----- src/main/java/net/snackbag/vera/Vera.java | 6 +++--- src/main/java/net/snackbag/vera/core/VeraApp.java | 10 +++++----- ...PositioningFlag.java => VAppPositioningFlag.java} | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) rename src/main/java/net/snackbag/vera/flag/{VWindowPositioningFlag.java => VAppPositioningFlag.java} (96%) diff --git a/src/main/java/net/snackbag/mcvera/MCVeraData.java b/src/main/java/net/snackbag/mcvera/MCVeraData.java index 6af78075..906e744d 100644 --- a/src/main/java/net/snackbag/mcvera/MCVeraData.java +++ b/src/main/java/net/snackbag/mcvera/MCVeraData.java @@ -2,7 +2,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VAppFlag; -import net.snackbag.vera.flag.VWindowPositioningFlag; +import net.snackbag.vera.flag.VAppPositioningFlag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +11,7 @@ public class MCVeraData { public static LinkedHashSet applications = new LinkedHashSet<>(); - public static HashMap> visibleApplications = new HashMap<>(); + public static HashMap> visibleApplications = new HashMap<>(); public static HashMap> appFlags = new HashMap<>(); public static int appsWithMouseRequired = 0; diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 8e897dd2..36a604bb 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -14,7 +14,7 @@ import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VAppFlag; -import net.snackbag.vera.flag.VWindowPositioningFlag; +import net.snackbag.vera.flag.VAppPositioningFlag; import net.snackbag.vera.widget.VWidget; import org.lwjgl.opengl.GL11; @@ -97,7 +97,7 @@ public void renderApp(VeraApp app) { if (!blendEnabled) RenderSystem.disableBlend(); } - public void renderApps(VWindowPositioningFlag flag) { + public void renderApps(VAppPositioningFlag flag) { if (!MinecraftClient.getInstance().isRunning()) return; LinkedHashSet apps = MCVeraData.visibleApplications.getOrDefault(flag, new LinkedHashSet<>()); diff --git a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java index 754b51c4..e373d140 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/GameRendererMixin.java @@ -3,7 +3,7 @@ import net.minecraft.client.render.GameRenderer; import net.snackbag.vera.InternalVera; import net.snackbag.vera.Vera; -import net.snackbag.vera.flag.VWindowPositioningFlag; +import net.snackbag.vera.flag.VAppPositioningFlag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -16,23 +16,23 @@ public abstract class GameRendererMixin { @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getOverlay()Lnet/minecraft/client/gui/screen/Overlay;", ordinal = 0, shift = At.Shift.BEFORE)) private void mcvera$renderAboveHud(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.ABOVE_HUD); + Vera.renderer.renderApps(VAppPositioningFlag.ABOVE_HUD); } @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V")) private void mcvera$renderOnGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.GUI); + Vera.renderer.renderApps(VAppPositioningFlag.GUI); } @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.AFTER)) private void mcvera$renderAboveGUI(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.ABOVE_GUI); + Vera.renderer.renderApps(VAppPositioningFlag.ABOVE_GUI); } @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/toast/ToastManager;draw(Lnet/minecraft/client/gui/DrawContext;)V", shift = At.Shift.AFTER)) private void mcvera$renderScreenAndTop(float tickDelta, long startTime, boolean tick, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.SCREEN); - Vera.renderer.renderApps(VWindowPositioningFlag.TOP); + Vera.renderer.renderApps(VAppPositioningFlag.SCREEN); + Vera.renderer.renderApps(VAppPositioningFlag.TOP); List tasks = new ArrayList<>(InternalVera.getScheduledTasks()); InternalVera.clearScheduledTasks(); diff --git a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java index 748fae32..836bb4bc 100644 --- a/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java +++ b/src/main/java/net/snackbag/mcvera/mixin/InGameHudMixin.java @@ -3,7 +3,7 @@ import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; import net.snackbag.vera.Vera; -import net.snackbag.vera.flag.VWindowPositioningFlag; +import net.snackbag.vera.flag.VAppPositioningFlag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -18,21 +18,21 @@ public abstract class InGameHudMixin { @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;enableBlend()V", shift = At.Shift.AFTER, ordinal = 0, remap = false), method = "render") private void mcvera$renderBelowVignette(DrawContext context, float tickDelta, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_VIGNETTE); + Vera.renderer.renderApps(VAppPositioningFlag.BELOW_VIGNETTE); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getLastFrameDuration()F"), method = "render") private void mcvera$renderBelowOverlays(DrawContext context, float tickDelta, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_OVERLAYS); + Vera.renderer.renderApps(VAppPositioningFlag.BELOW_OVERLAYS); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;getCurrentGameMode()Lnet/minecraft/world/GameMode;", ordinal = 0, shift = At.Shift.BEFORE), method = "render") private void mcvera$renderBelowHud(DrawContext context, float tickDelta, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.BELOW_HUD); + Vera.renderer.renderApps(VAppPositioningFlag.BELOW_HUD); } @Inject(at = @At(value = "TAIL"), method = "renderHotbar") private void mcvera$renderHud(float tickDelta, DrawContext context, CallbackInfo ci) { - Vera.renderer.renderApps(VWindowPositioningFlag.HUD); + Vera.renderer.renderApps(VAppPositioningFlag.HUD); } } diff --git a/src/main/java/net/snackbag/vera/Vera.java b/src/main/java/net/snackbag/vera/Vera.java index a411d3ba..eb3ce03e 100644 --- a/src/main/java/net/snackbag/vera/Vera.java +++ b/src/main/java/net/snackbag/vera/Vera.java @@ -7,7 +7,7 @@ import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VAppFlag; -import net.snackbag.vera.flag.VWindowPositioningFlag; +import net.snackbag.vera.flag.VAppPositioningFlag; import org.jetbrains.annotations.Nullable; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; @@ -39,7 +39,7 @@ public static void forVisibleAndAllowedApps(Consumer handler) { handler.accept(topHierarchy); } - for (VWindowPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { + for (VAppPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { for (VeraApp app : MCVeraData.visibleApplications.get(flag)) { if (handledApps.contains(app) || app.hasFlag(VAppFlag.HIERARCHIC)) continue; @@ -50,7 +50,7 @@ public static void forVisibleAndAllowedApps(Consumer handler) { } public static void forAllVisibleApps(Consumer handler) { - for (VWindowPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { + for (VAppPositioningFlag flag : MCVeraData.visibleApplications.keySet()) { for (VeraApp app : MCVeraData.visibleApplications.get(flag)) { handler.accept(app); } diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index be9152c7..7fb0819c 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -7,7 +7,7 @@ import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VAppFlag; -import net.snackbag.vera.flag.VWindowPositioningFlag; +import net.snackbag.vera.flag.VAppPositioningFlag; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.util.VGeometry; import net.snackbag.vera.widget.VWidget; @@ -32,7 +32,7 @@ public abstract class VeraApp { private boolean visible; private @Nullable VWidget focusedWidget; - private VWindowPositioningFlag positioning; + private VAppPositioningFlag positioning; public VeraApp() { this(true); @@ -54,7 +54,7 @@ public VeraApp(boolean mouseRequired) { this.y = 0; this.visible = false; - setPositioning(VWindowPositioningFlag.SCREEN); + setPositioning(VAppPositioningFlag.SCREEN); } public void setCursorVisible(boolean cursorVisible) { @@ -296,11 +296,11 @@ public void setCursorShape(VCursorShape cursorShape) { ); } - public VWindowPositioningFlag getPositioning() { + public VAppPositioningFlag getPositioning() { return positioning; } - public void setPositioning(VWindowPositioningFlag positioning) { + public void setPositioning(VAppPositioningFlag positioning) { // make sure hashmaps exist if (!MCVeraData.visibleApplications.containsKey(this.positioning)) MCVeraData.visibleApplications.put(this.positioning, new LinkedHashSet<>()); diff --git a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java b/src/main/java/net/snackbag/vera/flag/VAppPositioningFlag.java similarity index 96% rename from src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java rename to src/main/java/net/snackbag/vera/flag/VAppPositioningFlag.java index 0ddebd8b..f7487d3d 100644 --- a/src/main/java/net/snackbag/vera/flag/VWindowPositioningFlag.java +++ b/src/main/java/net/snackbag/vera/flag/VAppPositioningFlag.java @@ -1,6 +1,6 @@ package net.snackbag.vera.flag; -public enum VWindowPositioningFlag { +public enum VAppPositioningFlag { /** * Deepest render position, renders even under the vignette */ From 7654ec821c18f066f08a870b7c5128e373dbe8e8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 18:35:29 +0100 Subject: [PATCH 572/661] better naming for methods --- src/main/java/net/snackbag/vera/core/VColor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index ab5a807f..1e64d89b 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -72,19 +72,19 @@ public float opacity() { return opacity; } - public float oneRed() { + public float normRed() { return (float) red / 255; } - public float oneGreen() { + public float normGreen() { return (float) green / 255; } - public float oneBlue() { + public float normBlue() { return (float) blue / 255; } - public int opacityToAlpha() { + public int denormalizedOpacity() { return (int) (opacity * 255); } From 8b024fea83e612c39ae8c49852ea5b6f8add43e2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 19:02:04 +0100 Subject: [PATCH 573/661] toInt -> toIntArgb --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 4 ++-- src/main/java/net/snackbag/vera/core/VColor.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 36a604bb..ef0a4c4d 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -38,7 +38,7 @@ public void drawRect(VeraApp app, int x, int y, int width, int height, double ro stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((float) rotation)); stack.translate(-width / 2f, -height / 2f, 0); - drawContext.fill(0, 0, width, height, color.toInt()); + drawContext.fill(0, 0, width, height, color.toIntArgb()); stack.pop(); } @@ -53,7 +53,7 @@ public void drawText(VeraApp app, int x, int y, double rotation, String text, VF MinecraftClient.getInstance().textRenderer, Text.literal(text).setStyle(Style.EMPTY.withFont(new Identifier(font.getName()))), 0, 0, // x and y are handled by translate - font.getColor().toInt(), + font.getColor().toIntArgb(), false ); diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 1e64d89b..7c7a4298 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -132,7 +132,7 @@ public boolean isSame(VColor color) { return same(color.red, color.green, color.blue, color.opacity); } - public int toInt() { + public int toIntArgb() { int alpha = (int) (opacity * 255); return (alpha << 24) | (red << 16) | (green << 8) | blue; } From 9cd4c0a99869912718922da428a5374c22155c37 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 19:38:38 +0100 Subject: [PATCH 574/661] add accessor --- .../snackbag/mcvera/mixin/DrawContextAccessor.java | 11 +++++++++++ src/main/resources/mcvera.mixins.json | 2 ++ 2 files changed, 13 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/mixin/DrawContextAccessor.java diff --git a/src/main/java/net/snackbag/mcvera/mixin/DrawContextAccessor.java b/src/main/java/net/snackbag/mcvera/mixin/DrawContextAccessor.java new file mode 100644 index 00000000..e3f2befd --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/mixin/DrawContextAccessor.java @@ -0,0 +1,11 @@ +package net.snackbag.mcvera.mixin; + +import net.minecraft.client.gui.DrawContext; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(DrawContext.class) +public interface DrawContextAccessor { + @Invoker("tryDraw") + void vera$invokeTryDraw(); +} diff --git a/src/main/resources/mcvera.mixins.json b/src/main/resources/mcvera.mixins.json index f762fbef..2288d01b 100644 --- a/src/main/resources/mcvera.mixins.json +++ b/src/main/resources/mcvera.mixins.json @@ -9,6 +9,8 @@ "defaultRequire": 1 }, "client": [ + "DrawContextAccessor", + "DrawContextMixin", "GameRendererMixin", "InGameHudMixin", From ad8605414bea4bfb5dd9727905e92c3a19818a7a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 19:48:34 +0100 Subject: [PATCH 575/661] add quad rendering api --- .../snackbag/mcvera/impl/MCVeraRenderer.java | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index ef0a4c4d..b76bfa7f 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -3,12 +3,14 @@ import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.render.*; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.math.RotationAxis; import net.snackbag.mcvera.MCVeraData; +import net.snackbag.mcvera.mixin.DrawContextAccessor; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; @@ -16,6 +18,7 @@ import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VAppPositioningFlag; import net.snackbag.vera.widget.VWidget; +import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; import java.util.ArrayList; @@ -26,6 +29,10 @@ public class MCVeraRenderer { public static DrawContext drawContext = null; + // + // Basic rendering + // + public void drawRect(VeraApp app, int x, int y, int width, int height, double rotation, VColor color) { MatrixStack stack = drawContext.getMatrices(); stack.push(); @@ -64,6 +71,215 @@ public void drawImage(VeraApp app, int x, int y, int width, int height, double r drawContext.drawTexture(path, x + app.getX(), y + app.getY(), 0, 0, width, height, width, height); } + /** + * Renders a solid-colored quad to the GUI render layer. + * + *

Vertices must be provided in counter-clockwise order in screen space + * (Minecraft GUI coordinates, where Y increases downward):

+ * + *
+     * v1 ── v4
+     * │     │
+     * v2 ── v3
+     * 
+ * + *
    + *
  • v1 = top-left
  • + *
  • v2 = bottom-left
  • + *
  • v3 = bottom-right
  • + *
  • v4 = top-right
  • + *
+ * + *

No validation or reordering is performed. Incorrect vertex order or + * duplicated vertices will result in visual artifacts or no output.

+ * + *

All vertices are rendered with the same color.

+ * + *

This method renders using {@code RenderLayer.getGui()} and immediately + * flushes the vertex buffer.

+ * + * @param v1x top-left x + * @param v1y top-left y + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param v4x top-right x + * @param v4y top-right y + * @param color color applied to all vertices + */ + public void renderColQuad( + int v1x, int v1y, + int v2x, int v2y, + int v3x, int v3y, + int v4x, int v4y, + VColor color + ) { + renderColQuad(v1x, v1y, color, v2x, v2y, color, v3x, v3y, color, v4x, v4y, color); + } + + /** + * Renders a quad to the GUI render layer with per-vertex colors. + * + *

Vertices must be provided in counter-clockwise order in screen space + * (Minecraft GUI coordinates, where Y increases downward):

+ * + *
+     * v1 ── v4
+     * │     │
+     * v2 ── v3
+     * 
+ * + *

No validation or reordering is performed.

+ * + *

This method renders using {@code RenderLayer.getGui()} and immediately + * flushes the vertex buffer.

+ * + * @param v1x top-left x + * @param v1y top-left y + * @param v1col color at v1 + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param v2col color at v2 + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param v3col color at v3 + * @param v4x top-right x + * @param v4y top-right y + * @param v4col color at v4 + */ + public void renderColQuad( + int v1x, int v1y, VColor v1col, + int v2x, int v2y, VColor v2col, + int v3x, int v3y, VColor v3col, + int v4x, int v4y, VColor v4col + ) { + Matrix4f matrix = drawContext.getMatrices().peek().getPositionMatrix(); + + VertexConsumer consumer = drawContext.getVertexConsumers().getBuffer(RenderLayer.getGui()); + consumer.vertex(matrix, (float) v1x, (float) v1y, 0f).color(v1col.toIntArgb()).next(); + consumer.vertex(matrix, (float) v2x, (float) v2y, 0f).color(v2col.toIntArgb()).next(); + consumer.vertex(matrix, (float) v3x, (float) v3y, 0f).color(v3col.toIntArgb()).next(); + consumer.vertex(matrix, (float) v4x, (float) v4y, 0f).color(v4col.toIntArgb()).next(); + + ((DrawContextAccessor) drawContext).vera$invokeTryDraw(); + } + + /** + * Renders a textured quad to the GUI render layer using the full texture. + * + *

The texture is automatically bound via the provided + * {@link net.minecraft.util.Identifier}.

+ * + *

Blending is automatically enabled and disabled based on + * {@code hasTransparentParts}.

+ * + *

Vertices must be provided in counter-clockwise order in screen space + * (Minecraft GUI coordinates, where Y increases downward):

+ * + *
+     * v1 ── v4
+     * │     │
+     * v2 ── v3
+     * 
+ * + *

Texture coordinates are automatically mapped to the full texture + * (u,v in the range 0.0–1.0).

+ * + *

The vertex buffer is flushed immediately.

+ * + * @param hasTransparentParts whether the texture has transparent parts; handles blending + * @param texture texture identifier to bind + * @param v1x top-left x + * @param v1y top-left y + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param v4x top-right x + * @param v4y top-right y + */ + public void renderTexQuad( + boolean hasTransparentParts, + Identifier texture, + int v1x, int v1y, + int v2x, int v2y, + int v3x, int v3y, + int v4x, int v4y + ) { + renderTexQuad( + hasTransparentParts, texture, + v1x, v1y, 0.0f, 0.0f, + v2x, v2y, 0.0f, 1.0f, + v3x, v3y, 1.0f, 1.0f, + v4x, v4y, 1.0f, 0.0f + ); + } + + /** + * Renders a textured quad to the GUI render layer with per-vertex UVs. + * + *

The texture is automatically bound via the provided + * {@link net.minecraft.util.Identifier}.

+ * + *

Blending is automatically enabled and disabled based on + * {@code hasTransparentParts}.

+ * + *

Vertices must be provided in counter-clockwise order in screen space + * (Minecraft GUI coordinates, where Y increases downward).

+ * + *

No validation or UV normalization is performed.

+ * + *

The vertex buffer is flushed immediately.

+ * + * @param hasTransparentParts whether the texture has transparent parts; handles blending + * @param texture texture identifier to bind + * @param v1x top-left x + * @param v1y top-left y + * @param u1 texture u at v1 + * @param v1t texture v at v1 + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param u2 texture u at v2 + * @param v2t texture v at v2 + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param u3 texture u at v3 + * @param v3t texture v at v3 + * @param v4x top-right x + * @param v4y top-right y + * @param u4 texture u at v4 + * @param v4t texture v at v4 + */ + public void renderTexQuad( + boolean hasTransparentParts, + Identifier texture, + int v1x, int v1y, float u1, float v1t, + int v2x, int v2y, float u2, float v2t, + int v3x, int v3y, float u3, float v3t, + int v4x, int v4y, float u4, float v4t + ) { + RenderSystem.setShaderTexture(0, texture); + RenderSystem.setShader(GameRenderer::getPositionTexProgram); + if (hasTransparentParts) RenderSystem.enableBlend(); + + Matrix4f matrix = drawContext.getMatrices().peek().getPositionMatrix(); + + BufferBuilder buf = Tessellator.getInstance().getBuffer(); + buf.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); + buf.vertex(matrix, v1x, v1y, 0f).texture(u1, v1t).next(); + buf.vertex(matrix, v2x, v2y, 0f).texture(u2, v2t).next(); + buf.vertex(matrix, v3x, v3y, 0f).texture(u3, v3t).next(); + buf.vertex(matrix, v4x, v4y, 0f).texture(u4, v4t).next(); + + BufferRenderer.drawWithGlobalProgram(buf.end()); + if (hasTransparentParts) RenderSystem.disableBlend(); + } + + // + // Apps + // + public void renderApp(VeraApp app) { boolean blendEnabled = GL11.glIsEnabled(GL11.GL_BLEND); From 545bdaa734544ad0dd7109e09380ef79aaee4d9c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 19:48:41 +0100 Subject: [PATCH 576/661] add quad rendering test --- .../net/snackbag/mcvera/InternalCommands.java | 5 +++ .../mcvera/test/QuadTestApplication.java | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/java/net/snackbag/mcvera/test/QuadTestApplication.java diff --git a/src/main/java/net/snackbag/mcvera/InternalCommands.java b/src/main/java/net/snackbag/mcvera/InternalCommands.java index 83346ce6..a6b921e5 100644 --- a/src/main/java/net/snackbag/mcvera/InternalCommands.java +++ b/src/main/java/net/snackbag/mcvera/InternalCommands.java @@ -38,6 +38,10 @@ public static void register( HierarchyTest.INSTANCE.start(); return 1; })) + .then(ClientCommandManager.literal("quads").executes(ctx -> { + QuadTestApplication.INSTANCE.show(); + return 1; + })) .then(ClientCommandManager.literal("demo").executes(ctx -> { DemoMod.init(); return 1; @@ -50,6 +54,7 @@ public static void register( LayoutTestApplication.INSTANCE = new LayoutTestApplication(); LayoutCenteringTestApplication.INSTANCE = new LayoutCenteringTestApplication(); HierarchyTest.INSTANCE = new HierarchyTest(); + QuadTestApplication.INSTANCE = new QuadTestApplication(); return 1; }) ) diff --git a/src/main/java/net/snackbag/mcvera/test/QuadTestApplication.java b/src/main/java/net/snackbag/mcvera/test/QuadTestApplication.java new file mode 100644 index 00000000..216fbf2f --- /dev/null +++ b/src/main/java/net/snackbag/mcvera/test/QuadTestApplication.java @@ -0,0 +1,36 @@ +package net.snackbag.mcvera.test; + +import net.minecraft.util.Identifier; +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.event.VShortcut; + +public class QuadTestApplication extends VeraApp { + public static QuadTestApplication INSTANCE = new QuadTestApplication(); + + @Override + public void init() { + new VShortcut(this, "escape", this::hide); + } + + @Override + public void render() { + Vera.renderer.renderColQuad( + 0, 0, + 0, 100, + 100, 100, + 100, 0, + VColor.MC_RED + ); + + Vera.renderer.renderTexQuad( + true, + new Identifier("mcvera", "icon.png"), + 100, 0, + 120, 100, + 220, 100, + 200, 0 + ); + } +} From 316d4befe7cf4bcc6700a090d6976faae22317c8 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 19:59:10 +0100 Subject: [PATCH 577/661] 1.9.2 -> 2.0.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 23fb2960..254fb614 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10 loader_version=0.16.7 # Mod Properties -mod_version=1.9.2 +mod_version=2.0.0 maven_group=net.snackbag.mcvera archives_base_name=mcvera From c064f6fd33336dd888104107b95689fa1ae53a1d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 19:59:19 +0100 Subject: [PATCH 578/661] update all 1.10 things to 2.0 --- src/main/java/net/snackbag/vera/core/VColor.java | 16 ++++++++-------- .../java/net/snackbag/vera/event/VShortcut.java | 6 +++--- .../snackbag/vera/modifier/VPaddingWidget.java | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 7c7a4298..85d77d06 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -92,14 +92,14 @@ public boolean isTransparent() { return opacity == 0; } - @Deprecated(since = "1.10", forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.11") + @Deprecated(since = "2.0", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "2.1") public boolean sameColors(int red, int green, int blue) { return hasSameColors(red, green, blue); } - @Deprecated(since = "1.10", forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.11") + @Deprecated(since = "2.0", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "2.1") public boolean sameColors(VColor color) { return hasSameColors(color); } @@ -112,14 +112,14 @@ public boolean hasSameColors(VColor color) { return sameColors(color.red, color.green, color.blue); } - @Deprecated(since = "1.10", forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.11") + @Deprecated(since = "2.0", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "2.1") public boolean same(int red, int green, int blue, float opacity) { return isSame(red, green, blue, opacity); } - @Deprecated(since = "1.10", forRemoval = true) - @ApiStatus.ScheduledForRemoval(inVersion = "1.11") + @Deprecated(since = "2.0", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "2.1") public boolean same(VColor color) { return isSame(color); } diff --git a/src/main/java/net/snackbag/vera/event/VShortcut.java b/src/main/java/net/snackbag/vera/event/VShortcut.java index 81848dd5..d519f207 100644 --- a/src/main/java/net/snackbag/vera/event/VShortcut.java +++ b/src/main/java/net/snackbag/vera/event/VShortcut.java @@ -49,11 +49,11 @@ public void run() { } /** - * Deprecated since version 1.10, will be removed in 1.11. No longer needed since the app now already receives the + * Deprecated since version 2.0, will be removed in 2.1. No longer needed since the app now already receives the * shortcut on shortcut initialization. */ - @Deprecated(forRemoval = true, since = "1.10") - @ApiStatus.ScheduledForRemoval(inVersion = "1.11") + @Deprecated(forRemoval = true, since = "2.0") + @ApiStatus.ScheduledForRemoval(inVersion = "2.1") public VShortcut alsoAdd() { app.addShortcut(this); return this; diff --git a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java index 266cac8e..346a2d53 100644 --- a/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java +++ b/src/main/java/net/snackbag/vera/modifier/VPaddingWidget.java @@ -3,8 +3,8 @@ import net.snackbag.vera.core.v4.V4Int; import org.jetbrains.annotations.ApiStatus; -@ApiStatus.ScheduledForRemoval(inVersion = "1.11") -@Deprecated(forRemoval = true, since = "1.10") +@ApiStatus.ScheduledForRemoval(inVersion = "2.1") +@Deprecated(forRemoval = true, since = "2.0") public interface VPaddingWidget { V4Int getPadding(); From 1e00099510ff2f8fec923dd44651041b4f70be3c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:45:08 +0100 Subject: [PATCH 579/661] prepare rendering 2.0 --- .../snackbag/mcvera/impl/MCVeraRenderer.java | 161 +++++++++++------- 1 file changed, 102 insertions(+), 59 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index b76bfa7f..33d234d4 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -30,36 +30,70 @@ public class MCVeraRenderer { public static DrawContext drawContext = null; // - // Basic rendering + // Widget rendering // - public void drawRect(VeraApp app, int x, int y, int width, int height, double rotation, VColor color) { + public void pushContext(VWidget.RenderContext ctx) { MatrixStack stack = drawContext.getMatrices(); stack.push(); - float centerX = x + width / 2f; - float centerY = y + height / 2f; + float wMod = (ctx.width() / 2f) * (ctx.scale() - 1); + float hMod = (ctx.height() / 2f) * (ctx.scale() - 1); - stack.translate(app.getX(), app.getY(), 0); - stack.translate(centerX, centerY, 0); - stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((float) rotation)); - stack.translate(-width / 2f, -height / 2f, 0); + stack.translate(ctx.x() - wMod, ctx.y() - hMod, 0f); + stack.scale(ctx.scale(), ctx.scale(), 1.0f); + } - drawContext.fill(0, 0, width, height, color.toIntArgb()); + public void popContext() { + drawContext.getMatrices().pop(); + } + + public void drawRect(VWidget.RenderContext ctx, int x, int y, int width, int height, VColor color) { + drawRect( + x, y, + width, height, + color + ); + } + public void drawText(VWidget.RenderContext ctx, int x, int y, String text, VFont font) { + MatrixStack stack = drawContext.getMatrices(); + stack.push(); + + drawText( + x, y, + text, font + ); stack.pop(); } - public void drawText(VeraApp app, int x, int y, double rotation, String text, VFont font) { + public void drawImage(VWidget.RenderContext ctx, int x, int y, int width, int height, Identifier path) { + drawImage( + x, + y, + width, height, + path + ); + } + + // + // Basic rendering + // + + public void drawRect(int x, int y, int width, int height, VColor color) { + drawContext.fill(x, y, x + width, y + height, color.toIntArgb()); + } + + public void drawText(int x, int y, String text, VFont font) { float scaleFactor = font.getSize() / 16.0f; drawContext.getMatrices().push(); - drawContext.getMatrices().translate(x + app.getX(), y + app.getY(), 0); + drawContext.getMatrices().translate(x, y, 0); drawContext.getMatrices().scale(scaleFactor, scaleFactor, 1.0f); drawContext.drawText( MinecraftClient.getInstance().textRenderer, Text.literal(text).setStyle(Style.EMPTY.withFont(new Identifier(font.getName()))), - 0, 0, // x and y are handled by translate + 0, 0, font.getColor().toIntArgb(), false ); @@ -67,10 +101,14 @@ public void drawText(VeraApp app, int x, int y, double rotation, String text, VF drawContext.getMatrices().pop(); } - public void drawImage(VeraApp app, int x, int y, int width, int height, double rotation, Identifier path) { - drawContext.drawTexture(path, x + app.getX(), y + app.getY(), 0, 0, width, height, width, height); + public void drawImage(int x, int y, int width, int height, Identifier path) { + drawContext.drawTexture(path, x, y, 0, 0, width, height, width, height); } + // + // Low-level rendering + // + /** * Renders a solid-colored quad to the GUI render layer. * @@ -98,14 +136,14 @@ public void drawImage(VeraApp app, int x, int y, int width, int height, double r *

This method renders using {@code RenderLayer.getGui()} and immediately * flushes the vertex buffer.

* - * @param v1x top-left x - * @param v1y top-left y - * @param v2x bottom-left x - * @param v2y bottom-left y - * @param v3x bottom-right x - * @param v3y bottom-right y - * @param v4x top-right x - * @param v4y top-right y + * @param v1x top-left x + * @param v1y top-left y + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param v4x top-right x + * @param v4y top-right y * @param color color applied to all vertices */ public void renderColQuad( @@ -135,17 +173,17 @@ public void renderColQuad( *

This method renders using {@code RenderLayer.getGui()} and immediately * flushes the vertex buffer.

* - * @param v1x top-left x - * @param v1y top-left y + * @param v1x top-left x + * @param v1y top-left y * @param v1col color at v1 - * @param v2x bottom-left x - * @param v2y bottom-left y + * @param v2x bottom-left x + * @param v2y bottom-left y * @param v2col color at v2 - * @param v3x bottom-right x - * @param v3y bottom-right y + * @param v3x bottom-right x + * @param v3y bottom-right y * @param v3col color at v3 - * @param v4x top-right x - * @param v4y top-right y + * @param v4x top-right x + * @param v4y top-right y * @param v4col color at v4 */ public void renderColQuad( @@ -189,15 +227,15 @@ public void renderColQuad( *

The vertex buffer is flushed immediately.

* * @param hasTransparentParts whether the texture has transparent parts; handles blending - * @param texture texture identifier to bind - * @param v1x top-left x - * @param v1y top-left y - * @param v2x bottom-left x - * @param v2y bottom-left y - * @param v3x bottom-right x - * @param v3y bottom-right y - * @param v4x top-right x - * @param v4y top-right y + * @param texture texture identifier to bind + * @param v1x top-left x + * @param v1y top-left y + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param v4x top-right x + * @param v4y top-right y */ public void renderTexQuad( boolean hasTransparentParts, @@ -233,23 +271,23 @@ public void renderTexQuad( *

The vertex buffer is flushed immediately.

* * @param hasTransparentParts whether the texture has transparent parts; handles blending - * @param texture texture identifier to bind - * @param v1x top-left x - * @param v1y top-left y - * @param u1 texture u at v1 - * @param v1t texture v at v1 - * @param v2x bottom-left x - * @param v2y bottom-left y - * @param u2 texture u at v2 - * @param v2t texture v at v2 - * @param v3x bottom-right x - * @param v3y bottom-right y - * @param u3 texture u at v3 - * @param v3t texture v at v3 - * @param v4x top-right x - * @param v4y top-right y - * @param u4 texture u at v4 - * @param v4t texture v at v4 + * @param texture texture identifier to bind + * @param v1x top-left x + * @param v1y top-left y + * @param u1 texture u at v1 + * @param v1t texture v at v1 + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param u2 texture u at v2 + * @param v2t texture v at v2 + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param u3 texture u at v3 + * @param v3t texture v at v3 + * @param v4x top-right x + * @param v4y top-right y + * @param u4 texture u at v4 + * @param v4t texture v at v4 */ public void renderTexQuad( boolean hasTransparentParts, @@ -299,9 +337,14 @@ public void renderApp(VeraApp app) { widget.animations.updateLifetimes(); if (widget.visibilityConditionsPassed()) { - widget.render(); - widget.renderBorder(); - widget.renderOverlay(); + VWidget.RenderContext ctx = widget.createRenderContext(); + pushContext(ctx); + + widget.render(ctx); + widget.renderBorder(ctx); + widget.renderOverlay(ctx); + + popContext(); } widget.afterRender(); From 59fbb21aaee709264c6a51722ee600ec69a7d9c3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:45:15 +0100 Subject: [PATCH 580/661] add scale tests --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 2a2d899e..cd212166 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -15,9 +15,6 @@ public class StyleTestApplication extends VeraApp { public static StyleTestApplication INSTANCE = new StyleTestApplication(); - private final VAnimation testAnimation = new VAnimation.Builder("test") - .keyframe(1000, 5000, frame -> frame.style("background-color", VColor.MC_RED)) - .build(); private final VAnimation longTestAnimation = new VAnimation.Builder("long_test") .loopMode(VLoopMode.FORWARD_REPEAT) .unwindTime(1000) @@ -48,6 +45,9 @@ public void init() { VLabel testLabel = new VLabel("hello there", 40, 10, this) .alsoAddClass("label") .alsoAdd(); + testLabel.setStyle("scale", StyleState.DEFAULT, 1.0f); + testLabel.setStyle("scale", StyleState.HOVERED, 2.0f); + testLabel.setStyle("transition", 100); testLabel.onMouseDragLeft((ctx) -> testLabel.move( testLabel.getX() + ctx.moveX(), From ff76edb988e13b96c92ac1c2bd65dbb81b3f9ab4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:45:56 +0100 Subject: [PATCH 581/661] add transparency changing + event --- .../java/net/snackbag/vera/event/VEvents.java | 2 ++ .../event/VTransparencyStateChangedEvent.java | 5 +++++ .../java/net/snackbag/vera/widget/VWidget.java | 15 +++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java diff --git a/src/main/java/net/snackbag/vera/event/VEvents.java b/src/main/java/net/snackbag/vera/event/VEvents.java index 2b6da3d5..97f6a54f 100644 --- a/src/main/java/net/snackbag/vera/event/VEvents.java +++ b/src/main/java/net/snackbag/vera/event/VEvents.java @@ -35,6 +35,8 @@ public static class Widget { public static final String DRAG_RIGHT_CLICK = "mouse-drag-right"; public static final String DRAG_MIDDLE_CLICK = "mouse-drag-middle"; + public static final String TRANSPARENCY_STATE_CHANGED = "transparency-state-changed"; + public static final String FOCUS_STATE_CHANGE = "focus-state-change"; public static final String FILES_DROPPED = "files-dropped"; } diff --git a/src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java b/src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java new file mode 100644 index 00000000..ac63598d --- /dev/null +++ b/src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java @@ -0,0 +1,5 @@ +package net.snackbag.vera.event; + +public interface VTransparencyStateChangedEvent { + void run(boolean transparency); +} diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 35816f41..c19d952e 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -20,6 +20,7 @@ public abstract class VWidget> extends VElement { public AnimationEngine animations = new AnimationEngine(this); protected double rotation; + protected boolean hasTransparency; public boolean focusOnClick = true; private boolean hovered = false; @@ -39,6 +40,7 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { super(app, x, y, width, height); this.rotation = 0; + this.hasTransparency = false; } public abstract void render(); @@ -265,6 +267,15 @@ public void rotate(double rotation) { this.rotation = rotation; } + public boolean hasTransparency() { + return hasTransparency; + } + + public void setHasTransparency(boolean hasTransparency) { + this.hasTransparency = hasTransparency; + events.fire(VEvents.Widget.TRANSPARENCY_STATE_CHANGED, hasTransparency); + } + public void update() { StyleState state = createStyleState(); @@ -363,6 +374,10 @@ public void onAnimationFinish(VAnimationFinishEvent runnable) { events.register(VEvents.Animation.FINISH, args -> runnable.run((VAnimation) args[0], (long) args[1])); } + public void onTransparencyStateChanged(VTransparencyStateChangedEvent runnable) { + events.register(VEvents.Widget.TRANSPARENCY_STATE_CHANGED, args -> runnable.run((boolean) args[0])); + } + @Override public void handleBuiltinEvent(String event, Object... args) { switch (event) { From 4292bbb2c7452b280e49b031ed6d324d4466860f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:46:46 +0100 Subject: [PATCH 582/661] proper ctx receival --- .../java/net/snackbag/vera/widget/VWidget.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index c19d952e..691c0f6e 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -43,7 +43,7 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { this.hasTransparency = false; } - public abstract void render(); + public abstract void render(RenderContext ctx); public int getHitboxX() { return getEffectiveX(); @@ -163,7 +163,17 @@ else if (DragHandler.isDragging() && DragHandler.target == this) { else return StyleState.DEFAULT; } - public void renderBorder() { + public RenderContext createRenderContext() { + StyleState state = createStyleState(); + return new RenderContext( + app.getX() + getEffectiveX(), app.getY() + getEffectiveY(), + getEffectiveWidth(), getEffectiveHeight(), + (float) rotation, getStyle("scale", state), + hasTransparency + ); + } + + public void renderBorder(RenderContext ctx) { // TODO: [Render Rework] Better border rendering StyleState state = createStyleState(); @@ -196,7 +206,7 @@ public void renderBorder() { } } - public void renderOverlay() { + public void renderOverlay(RenderContext ctx) { StyleState state = createStyleState(); Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), 0, getStyle("overlay", state)); From 6021ec33bbb9348ecf6d975d904d3c7808e55310 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:47:13 +0100 Subject: [PATCH 583/661] migrate to new rendering system --- .../java/net/snackbag/vera/core/VeraApp.java | 4 +-- .../net/snackbag/vera/widget/VCheckBox.java | 4 +-- .../net/snackbag/vera/widget/VDropdown.java | 31 +++++++++--------- .../java/net/snackbag/vera/widget/VImage.java | 4 +-- .../java/net/snackbag/vera/widget/VLabel.java | 20 ++++++------ .../net/snackbag/vera/widget/VLineInput.java | 32 ++++++++----------- .../java/net/snackbag/vera/widget/VRect.java | 4 +-- .../net/snackbag/vera/widget/VTabWidget.java | 13 +++----- .../net/snackbag/vera/widget/VWidget.java | 25 +++++++++------ 9 files changed, 68 insertions(+), 69 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 7fb0819c..88bde5d0 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -202,13 +202,13 @@ public VColor getBackgroundColor() { } public void render() { - Vera.renderer.drawRect(this, 0, 0, width, height, 0, backgroundColor); + Vera.renderer.drawRect(x, y, width, height, backgroundColor); } public void renderAfterWidgets() {} public void renderHierarchyOverlay() { - Vera.renderer.drawRect(this, 0, 0, width, height, 0, + Vera.renderer.drawRect(x, y, width, height, backgroundColor.isTransparent() ? VColor.black().withOpacity(0.2f) : backgroundColor.sub(40).withOpacity(0.2f) diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index a3631840..8a96a83d 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -33,11 +33,11 @@ public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTextu } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); Identifier texture = checked ? getStyle("src-checked", state) : getStyle("src", state); - Vera.renderer.drawImage(app, getX(), getY(), width, height, 0, texture); + Vera.renderer.drawImage(ctx, 0, 0, width, height, texture); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 6caf56b4..bd4af1db 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.List; +// TODO: [Render Rework] // TODO: Rewrite VDropdown from scratch // 16/7/2025 jesus christ what a shitty thing. dont even bother making this work nice. // rewrite scheduled for once we have VCompound @@ -35,18 +36,16 @@ public VDropdown(VeraApp app) { } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); VColor backgroundColor = getStyle("background-color", state); VFont font = getStyle("font", state); - - int x = getX(); - int y = getY(); + V4Int padding = getStyle("padding", createStyleState()); Vera.renderer.drawRect( - app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), - 0, backgroundColor + ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), + backgroundColor ); if (isFocused()) { @@ -54,33 +53,33 @@ app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight() boolean isHovered = hoveredItem != null && i == hoveredItem; Item item = items.get(i); - int textY = y + (i * (itemSpacing + font.getSize() / 2) + itemSpacing / 2); - int textX = (int) (item.icon == null ? x : x + font.getSize() * 0.7); + int textY = i * (itemSpacing + font.getSize() / 2) + itemSpacing / 2; + int textX = (int) (item.icon == null ? 0 : font.getSize() * 0.7); if (isHovered) { Vera.renderer.drawRect( - app, - getEffectiveX(), - y + (i * (itemSpacing + font.getSize() / 2)), + ctx, + 0, + i * (itemSpacing + font.getSize() / 2), getEffectiveWidth(), font.getSize() / 2 + itemSpacing, - 0, itemHoverColor + itemHoverColor ); } if (item.icon != null) { Vera.renderer.drawImage( - app, x, textY, + ctx, 0, textY, font.getSize() / 2, font.getSize() / 2, - 0, isHovered ? item.getHoverIcon() : item.getIcon() + isHovered ? item.getHoverIcon() : item.getIcon() ); } - Vera.renderer.drawText(app, textX, textY, 0, item.name, isHovered ? itemHoverFont : font); + Vera.renderer.drawText(ctx, textX, textY, item.name, isHovered ? itemHoverFont : font); } } else { - Vera.renderer.drawText(app, x, y, 0, getItems().get(selectedItem).name, font); + Vera.renderer.drawText(ctx, padding.get3(), padding.get1(), getItems().get(selectedItem).name, font); } } diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 0be47e49..33e7d82d 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -18,10 +18,10 @@ public VImage(String src, int width, int height, VeraApp app) { } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); Identifier src = getStyle("src", state); - Vera.renderer.drawImage(app, getX(), getY(), width, height, rotation, src); + Vera.renderer.drawImage(ctx, 0, 0, width, height, src); } } diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 870e740f..37cbf13b 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -74,30 +74,30 @@ public VColor.ColorModifier modifyColor(String key) { } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); VFont font = getStyle("font", state); VColor backgroundColor = getStyle("background-color", state); V4Int padding = getStyle("padding", state); + // TODO: render optimization; if background color is transparent, skip Vera.renderer.drawRect( - app, - getX(), - getY(), + ctx, + 0, + 0, getEffectiveWidth(), getEffectiveHeight(), - rotation, backgroundColor ); - int usualX = getX() + padding.get3(); - int usualY = getY() + padding.get1(); + int usualX = padding.get3(); + int usualY = padding.get1(); switch (alignment) { - case LEFT -> Vera.renderer.drawText(app, usualX, usualY, rotation, text, font); - case CENTER -> Vera.renderer.drawText(app, getX() + getWidth() / 2 - Vera.provider.getTextWidth(text, font) / 2, usualY, rotation, text, font); - case RIGHT -> Vera.renderer.drawText(app, getX() + getEffectiveWidth() - padding.get4() - Vera.provider.getTextWidth(text, font), usualY, rotation, text, font); + case LEFT -> Vera.renderer.drawText(ctx, usualX, usualY, text, font); + case CENTER -> Vera.renderer.drawText(ctx, getWidth() / 2 - Vera.provider.getTextWidth(text, font) / 2, usualY, text, font); + case RIGHT -> Vera.renderer.drawText(ctx, getEffectiveWidth() - padding.get4() - Vera.provider.getTextWidth(text, font), usualY, text, font); } } } diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 5f83b110..f830d1cd 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -14,6 +14,7 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; +// TODO: [Render Rework] Rewrite rendering from scratch public class VLineInput extends VWidget implements VHasFont, VHasPlaceholderFont { private String text; private String placeholderText; @@ -33,24 +34,21 @@ public VLineInput(VeraApp app) { } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); VFont font = getStyle("font", state); VFont placeholderFont = getStyle("placeholder-font", state); VColor backgroundColor = getStyle("background-color", state); VColor textSelectionColor = getStyle("select-color", state); - - int x = getX(); - int y = getY(); + V4Int padding = getStyle("padding", createStyleState()); Vera.renderer.drawRect( - app, - getEffectiveX() + app.getX(), - getEffectiveY() + app.getY(), + ctx, + app.getX(), + app.getY(), getEffectiveWidth(), getEffectiveHeight(), - rotation, backgroundColor ); @@ -61,29 +59,27 @@ public void render() { String beforeSelection = text.substring(0, selStart); String selectedText = text.substring(selStart, selEnd); - int selectionX = x + Vera.provider.getTextWidth(beforeSelection, font); + int selectionX = Vera.provider.getTextWidth(beforeSelection, font); Vera.renderer.drawRect( - app, + ctx, selectionX, - y, + 0, Vera.provider.getTextWidth(selectedText, font), Vera.provider.getTextHeight(text, font), - 0, textSelectionColor ); } - if (text.isEmpty()) Vera.renderer.drawText(app, x, y, 0, placeholderText, placeholderFont); - else Vera.renderer.drawText(app, x, y, 0, text, font); + if (text.isEmpty()) Vera.renderer.drawText(ctx, 0, 0, placeholderText, placeholderFont); + else Vera.renderer.drawText(ctx, padding.get3(), padding.get1(), text, font); if (isFocused() && textSelection.isClear() && (System.currentTimeMillis() / 500) % 2 == 0) { Vera.renderer.drawRect( - app, - x + Vera.provider.getTextWidth(text.substring(0, cursorPos), font), - y, + ctx, + Vera.provider.getTextWidth(text.substring(0, cursorPos), font), + 0, 1, Vera.provider.getTextHeight(text, font), - 0, getCursorColorSafe() ); } diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index d2e670b7..77528100 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -22,9 +22,9 @@ public VRect(VColor color, int x, int y, int width, int height, VeraApp app) { } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); - Vera.renderer.drawRect(app, getX(), getY(), width, height, rotation, getStyle("background-color", state)); + Vera.renderer.drawRect(ctx, 0, 0, width, height, getStyle("background-color", state)); } } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index f9aa0314..8494bdf3 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -22,7 +22,7 @@ public VTabWidget(VeraApp app) { } @Override - public void render() { + public void render(RenderContext ctx) { StyleState state = createStyleState(); VFont font = getStyle("font", state); @@ -34,23 +34,20 @@ public void render() { int marginX = 0; int i = -1; - int x = getX(); - int y = getY(); - for (String key : tabs.keySet()) { int textWidth = Vera.provider.getTextWidth(key, font); i++; marginX += itemSpacingLeft; - Vera.renderer.drawRect(app, - x + marginX - itemSpacingLeft, y, + Vera.renderer.drawRect(ctx, + marginX - itemSpacingLeft, 0, itemSpacingLeft + itemSpacingRight + textWidth, - getEffectiveHeight(), 0, + getEffectiveHeight(), activeTab != null && activeTab == i ? selectedBackgroundColor: defaultBackgroundColor ); - Vera.renderer.drawText(app, x + marginX, y + 2, 0, key, font); + Vera.renderer.drawText(ctx, marginX, 2, key, font); marginX += textWidth + itemSpacingRight; } diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 691c0f6e..b21bf44d 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -182,34 +182,34 @@ public void renderBorder(RenderContext ctx) { V4Int borderSize = getStyle("border-size", state); // Top - Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY() - borderSize.get1(), getEffectiveWidth(), borderSize.get1(), 0, borderColor.get1()); + Vera.renderer.drawRect(ctx, 0, -borderSize.get1(), getEffectiveWidth(), borderSize.get1(), borderColor.get1()); if (borderSize.get3() > 0) { - Vera.renderer.drawRect(app, getEffectiveX() - borderSize.get3(), getEffectiveY() - borderSize.get1(), borderSize.get3(), borderSize.get1(), 0, borderColor.get1()); + Vera.renderer.drawRect(ctx, -borderSize.get3(), -borderSize.get1(), borderSize.get3(), borderSize.get1(), borderColor.get1()); } // Bottom - Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY() + getEffectiveHeight(), getEffectiveWidth(), borderSize.get2(), 0, borderColor.get2()); + Vera.renderer.drawRect(ctx, 0, getEffectiveHeight(), getEffectiveWidth(), borderSize.get2(), borderColor.get2()); if (borderSize.get4() > 0) { - Vera.renderer.drawRect(app, getEffectiveX() + getEffectiveWidth(), getEffectiveY() + getEffectiveHeight(), borderSize.get4(), borderSize.get2(), 0, borderColor.get2()); + Vera.renderer.drawRect(ctx, getEffectiveWidth(), getEffectiveHeight(), borderSize.get4(), borderSize.get2(), borderColor.get2()); } // Left - Vera.renderer.drawRect(app, getEffectiveX() - borderSize.get3(), getEffectiveY(), borderSize.get3(), getEffectiveHeight(), 0, borderColor.get3()); + Vera.renderer.drawRect(ctx, -borderSize.get3(), 0, borderSize.get3(), getEffectiveHeight(), borderColor.get3()); if (borderSize.get2() > 0) { - Vera.renderer.drawRect(app, getEffectiveX() - borderSize.get3(), getEffectiveY() + getEffectiveHeight(), borderSize.get3(), borderSize.get2(), 0, borderColor.get3()); + Vera.renderer.drawRect(ctx, -borderSize.get3(), getEffectiveHeight(), borderSize.get3(), borderSize.get2(), borderColor.get3()); } // Right - Vera.renderer.drawRect(app, getEffectiveX() + getEffectiveWidth(), getEffectiveY(), borderSize.get4(), getEffectiveHeight(), 0, borderColor.get4()); + Vera.renderer.drawRect(ctx, getEffectiveWidth(), 0, borderSize.get4(), getEffectiveHeight(), borderColor.get4()); if (borderSize.get1() > 0) { - Vera.renderer.drawRect(app, getEffectiveX() + getEffectiveWidth(), getEffectiveY() - borderSize.get1(), borderSize.get4(), borderSize.get1(), 0, borderColor.get4()); + Vera.renderer.drawRect(ctx, getEffectiveWidth(), -borderSize.get1(), borderSize.get4(), borderSize.get1(), borderColor.get4()); } } public void renderOverlay(RenderContext ctx) { StyleState state = createStyleState(); - Vera.renderer.drawRect(app, getEffectiveX(), getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), 0, getStyle("overlay", state)); + Vera.renderer.drawRect(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), getStyle("overlay", state)); } public void beforeRender() { @@ -482,4 +482,11 @@ public T alsoAddTo(VLayout layout) { return (T) this; } + + public record RenderContext( + int x, int y, + int width, int height, + float rotation, float scale, + boolean hasTransparency + ) {} } From 7e0a0d0595ef3fb417df4ef848f25c7cd4e4615c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:47:20 +0100 Subject: [PATCH 584/661] reserve scale thing --- .../net/snackbag/vera/style/standard/WidgetStandardStyle.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index f909f7fd..d5cb42cb 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -22,6 +22,9 @@ public void apply(VStyleSheet sheet) { // Transition sheet.setKey(VWidget.class, "transition", 0); sheet.setKey(VWidget.class, "transition-easing", VEasings.getDefault()); + + // Rendering + sheet.setKey(VWidget.class, "scale", 1.0f); } @Override @@ -32,6 +35,7 @@ public void reserve(VStyleSheet sheet) { sheet.reserveType("border-size", StyleValueType.V4INT); sheet.reserveType("transition", StyleValueType.INT); sheet.reserveType("transition-easing", StyleValueType.EASING); + sheet.reserveType("scale", StyleValueType.FLOAT); // TODO: add background-color // TODO: add padding From b7cfc1f1dd7aeb674b7f6f212b6201287ef79a58 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Tue, 24 Feb 2026 23:59:12 +0100 Subject: [PATCH 585/661] add compat for int casting to float --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 206679d1..bc6fda9c 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -58,8 +58,11 @@ else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof VEasing) return EASING; - else if (val instanceof Integer) return INT; - else if (val instanceof Float || val instanceof Double) return FLOAT; + else if (val instanceof Integer || val instanceof Float || val instanceof Double) { + if (bias == FLOAT) return FLOAT; + if (val instanceof Integer) return INT; + return FLOAT; + } else if (val instanceof VColor) return COLOR; else if (val instanceof VFont) return FONT; else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); @@ -99,6 +102,7 @@ else if (value instanceof VColor[] v) { else if (value instanceof VColor v && to == V4COLOR) return new V4Color(v); else if (to == FLOAT && value instanceof Double v) return v.floatValue(); + else if (to == FLOAT && value instanceof Integer v) return v.floatValue(); return value; } From 4f95573ddb55e1f0797c38ccd381eee73313eb3c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:07:58 +0100 Subject: [PATCH 586/661] undo compat for int casting to float --- src/main/java/net/snackbag/vera/style/StyleValueType.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index bc6fda9c..206679d1 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -58,11 +58,8 @@ else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val else if (val instanceof Identifier) return IDENTIFIER; else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof VEasing) return EASING; - else if (val instanceof Integer || val instanceof Float || val instanceof Double) { - if (bias == FLOAT) return FLOAT; - if (val instanceof Integer) return INT; - return FLOAT; - } + else if (val instanceof Integer) return INT; + else if (val instanceof Float || val instanceof Double) return FLOAT; else if (val instanceof VColor) return COLOR; else if (val instanceof VFont) return FONT; else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); @@ -102,7 +99,6 @@ else if (value instanceof VColor[] v) { else if (value instanceof VColor v && to == V4COLOR) return new V4Color(v); else if (to == FLOAT && value instanceof Double v) return v.floatValue(); - else if (to == FLOAT && value instanceof Integer v) return v.floatValue(); return value; } From 03617f9661696fb83c0bdbc38301c9b6943bd3f9 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:12:48 +0100 Subject: [PATCH 587/661] write test for rotation --- .../java/net/snackbag/mcvera/test/TestApplication.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 4ae6f406..4a2f6280 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -10,6 +10,7 @@ import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.widget.*; import java.nio.file.Path; @@ -17,6 +18,10 @@ public class TestApplication extends VeraApp { public static TestApplication INSTANCE = new TestApplication(); + private final VAnimation rotationAnimation = new VAnimation.Builder("rotation") + .keyframe(1000, 0, frame -> frame.style("rotation", 360f)) + .build(); + public TestApplication() { super(); } @@ -119,7 +124,7 @@ public void init() { tabs.setActiveTab(0); VRect rotationRect = new VRect(VColor.black(), this).alsoAdd(); - rotationRect.rotate(45); + rotationRect.onLeftClick(() -> rotationRect.animate(rotationAnimation)); rotationRect.move(20, 200); } From d2f479e9e8613aa3f832caf35f994ecdc3793039 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:14:02 +0100 Subject: [PATCH 588/661] add rotation style --- .../vera/style/standard/WidgetStandardStyle.java | 5 +++++ src/main/java/net/snackbag/vera/widget/VWidget.java | 12 +----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java index d5cb42cb..a8d0974b 100644 --- a/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/WidgetStandardStyle.java @@ -25,17 +25,22 @@ public void apply(VStyleSheet sheet) { // Rendering sheet.setKey(VWidget.class, "scale", 1.0f); + sheet.setKey(VWidget.class, "rotation", 0.0f); } @Override public void reserve(VStyleSheet sheet) { sheet.reserveType("overlay", StyleValueType.COLOR); sheet.reserveType("cursor", StyleValueType.CURSOR); + sheet.reserveType("border-color", StyleValueType.V4COLOR); sheet.reserveType("border-size", StyleValueType.V4INT); + sheet.reserveType("transition", StyleValueType.INT); sheet.reserveType("transition-easing", StyleValueType.EASING); + sheet.reserveType("scale", StyleValueType.FLOAT); + sheet.reserveType("rotation", StyleValueType.FLOAT); // TODO: add background-color // TODO: add padding diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index b21bf44d..19046fa6 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -19,7 +19,6 @@ public abstract class VWidget> extends VElement { public AnimationEngine animations = new AnimationEngine(this); - protected double rotation; protected boolean hasTransparency; public boolean focusOnClick = true; @@ -39,7 +38,6 @@ public abstract class VWidget> extends VElement { public VWidget(int x, int y, int width, int height, VeraApp app) { super(app, x, y, width, height); - this.rotation = 0; this.hasTransparency = false; } @@ -168,7 +166,7 @@ public RenderContext createRenderContext() { return new RenderContext( app.getX() + getEffectiveX(), app.getY() + getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), - (float) rotation, getStyle("scale", state), + getStyle("rotation", state), getStyle("scale", state), hasTransparency ); } @@ -269,14 +267,6 @@ public boolean isAnyMouseButtonDown() { return leftClickDown || middleClickDown || rightClickDown; } - public double getRotation() { - return rotation; - } - - public void rotate(double rotation) { - this.rotation = rotation; - } - public boolean hasTransparency() { return hasTransparency; } From 4361c81f5e98c84c4b98ca664467ac0c1e213e15 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:14:17 +0100 Subject: [PATCH 589/661] add rotation rendering --- .../java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 33d234d4..09aafa62 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -40,6 +40,15 @@ public void pushContext(VWidget.RenderContext ctx) { float wMod = (ctx.width() / 2f) * (ctx.scale() - 1); float hMod = (ctx.height() / 2f) * (ctx.scale() - 1); + float xRot = ctx.x() + ctx.width() / 2f; + float yRot = ctx.y() + ctx.height() / 2f; + + // Rotation + stack.translate(xRot, yRot, 0f); + stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(ctx.rotation())); + stack.translate(-xRot, -yRot, 0f); + + // Scale & final positioning) stack.translate(ctx.x() - wMod, ctx.y() - hMod, 0f); stack.scale(ctx.scale(), ctx.scale(), 1.0f); } From 4525a9195d37a4bf8c2f399d960ce2e99333e92e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:20:43 +0100 Subject: [PATCH 590/661] fix rendering --- .../java/net/snackbag/vera/widget/VLineInput.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index f830d1cd..0f75fa9a 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -45,8 +45,8 @@ public void render(RenderContext ctx) { Vera.renderer.drawRect( ctx, - app.getX(), - app.getY(), + 0, + 0, getEffectiveWidth(), getEffectiveHeight(), backgroundColor @@ -63,21 +63,21 @@ public void render(RenderContext ctx) { Vera.renderer.drawRect( ctx, selectionX, - 0, + padding.get1(), Vera.provider.getTextWidth(selectedText, font), Vera.provider.getTextHeight(text, font), textSelectionColor ); } - if (text.isEmpty()) Vera.renderer.drawText(ctx, 0, 0, placeholderText, placeholderFont); + if (text.isEmpty()) Vera.renderer.drawText(ctx, padding.get3(), padding.get1(), placeholderText, placeholderFont); else Vera.renderer.drawText(ctx, padding.get3(), padding.get1(), text, font); if (isFocused() && textSelection.isClear() && (System.currentTimeMillis() / 500) % 2 == 0) { Vera.renderer.drawRect( ctx, - Vera.provider.getTextWidth(text.substring(0, cursorPos), font), - 0, + padding.get3() + Vera.provider.getTextWidth(text.substring(0, cursorPos), font), + padding.get1(), 1, Vera.provider.getTextHeight(text, font), getCursorColorSafe() From e61f01cf5ca7c92e5a46099b2e81bf5ece310adb Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:20:49 +0100 Subject: [PATCH 591/661] test rotation --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 4a2f6280..fd4a066b 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -124,7 +124,7 @@ public void init() { tabs.setActiveTab(0); VRect rotationRect = new VRect(VColor.black(), this).alsoAdd(); - rotationRect.onLeftClick(() -> rotationRect.animate(rotationAnimation)); + rotationRect.onLeftClick(() -> input.animate(rotationAnimation)); rotationRect.move(20, 200); } From a7a711de65e66340cc4e59033f938a1664868d2c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 00:20:57 +0100 Subject: [PATCH 592/661] better looking scaling --- .../java/net/snackbag/mcvera/test/StyleTestApplication.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index cd212166..8c8be57a 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -46,7 +46,8 @@ public void init() { .alsoAddClass("label") .alsoAdd(); testLabel.setStyle("scale", StyleState.DEFAULT, 1.0f); - testLabel.setStyle("scale", StyleState.HOVERED, 2.0f); + testLabel.setStyle("scale", StyleState.HOVERED, 1.2f); + testLabel.setStyle("scale", StyleState.CLICKED, 2.0f); testLabel.setStyle("transition", 100); testLabel.onMouseDragLeft((ctx) -> testLabel.move( @@ -60,7 +61,7 @@ public VStyleSheet createStyleSheet() { sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD.sub(80))); sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD), StyleState.HOVERED); - sheet.setKey("label", "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + sheet.setKey("label", "cursor", VCursorShape.POINTING_HAND); return sheet; } From cc63e6a56aeb44b2fd4d67dff63bbb94c21dea79 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 10:58:51 +0100 Subject: [PATCH 593/661] StyleState -> VStyleState --- .../com/example/demo/DemoApplication.java | 4 +- .../snackbag/mcvera/test/HierarchyTest.java | 4 +- .../mcvera/test/StyleTestApplication.java | 14 ++-- .../snackbag/mcvera/test/TestApplication.java | 6 +- .../net/snackbag/vera/modifier/VHasFont.java | 6 +- .../vera/modifier/VHasPlaceholderFont.java | 6 +- .../snackbag/vera/style/StyleContainer.java | 36 +++++----- .../net/snackbag/vera/style/VStyleSheet.java | 68 +++++++++---------- .../{StyleState.java => VStyleState.java} | 12 ++-- .../style/standard/CheckBoxStandardStyle.java | 4 +- .../style/standard/DropdownStandardStyle.java | 4 +- .../standard/LineInputStandardStyle.java | 4 +- .../standard/TabWidgetStandardStyle.java | 4 +- .../net/snackbag/vera/widget/VCheckBox.java | 4 +- .../net/snackbag/vera/widget/VDropdown.java | 6 +- .../java/net/snackbag/vera/widget/VImage.java | 4 +- .../java/net/snackbag/vera/widget/VLabel.java | 4 +- .../net/snackbag/vera/widget/VLineInput.java | 12 ++-- .../java/net/snackbag/vera/widget/VRect.java | 4 +- .../net/snackbag/vera/widget/VTabWidget.java | 8 +-- .../net/snackbag/vera/widget/VWidget.java | 46 ++++++------- 21 files changed, 130 insertions(+), 130 deletions(-) rename src/main/java/net/snackbag/vera/style/{StyleState.java => VStyleState.java} (83%) diff --git a/src/main/java/com/example/demo/DemoApplication.java b/src/main/java/com/example/demo/DemoApplication.java index e24ea2a3..f7c5fa89 100644 --- a/src/main/java/com/example/demo/DemoApplication.java +++ b/src/main/java/com/example/demo/DemoApplication.java @@ -8,7 +8,7 @@ import net.snackbag.vera.flag.VLayoutAlignmentFlag; import net.snackbag.vera.layout.VHLayout; import net.snackbag.vera.layout.VVLayout; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.widget.VLabel; public class DemoApplication extends VeraApp { @@ -35,7 +35,7 @@ public void init() { button.modifyFontColor().rgb(VColor.of(95, 180, 0)); button.setStyle("background-color", VColor.white()); button.setStyle("padding", 4); - button.setStyle("overlay", StyleState.HOVERED, VColor.white().withOpacity(0.5f)); + button.setStyle("overlay", VStyleState.HOVERED, VColor.white().withOpacity(0.5f)); button.setStyle("border-size", 1); button.setStyle("border-color", VColor.of(95, 180, 0)); button.setStyle("cursor", VCursorShape.POINTING_HAND); diff --git a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java index 4e78c344..1eeefc53 100644 --- a/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java +++ b/src/main/java/net/snackbag/mcvera/test/HierarchyTest.java @@ -6,7 +6,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VAppFlag; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.widget.VLabel; import net.snackbag.vera.widget.VRect; @@ -45,7 +45,7 @@ public void init() { setFlag(VAppFlag.HIERARCHIC, true); styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.white()).withSize(12)); - styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA).withSize(12), StyleState.HOVERED); + styleSheet.setKey(VLabel.class, "font", VFont.create().withColor(VColor.MC_AQUA).withSize(12), VStyleState.HOVERED); styleSheet.setKey(VLabel.class, "transition", 250); VRect mover = new VRect(VColor.black(), 0, 0, 100, 8, this).alsoAdd(); diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 8c8be57a..4a862b13 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -5,7 +5,7 @@ import net.snackbag.vera.core.VFont; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.style.animation.VLoopMode; @@ -38,16 +38,16 @@ public void init() { testRect.onRightClick(() -> testRect.animations.unwind(longTestAnimation)); testRect.setStyle("transition", 100); - testRect.setStyle("background-color", StyleState.HOVERED, VColor.white()); - testRect.setStyle("background-color", StyleState.CLICKED, VColor.MC_RED); + testRect.setStyle("background-color", VStyleState.HOVERED, VColor.white()); + testRect.setStyle("background-color", VStyleState.CLICKED, VColor.MC_RED); // Moving & classes VLabel testLabel = new VLabel("hello there", 40, 10, this) .alsoAddClass("label") .alsoAdd(); - testLabel.setStyle("scale", StyleState.DEFAULT, 1.0f); - testLabel.setStyle("scale", StyleState.HOVERED, 1.2f); - testLabel.setStyle("scale", StyleState.CLICKED, 2.0f); + testLabel.setStyle("scale", VStyleState.DEFAULT, 1.0f); + testLabel.setStyle("scale", VStyleState.HOVERED, 1.2f); + testLabel.setStyle("scale", VStyleState.CLICKED, 2.0f); testLabel.setStyle("transition", 100); testLabel.onMouseDragLeft((ctx) -> testLabel.move( @@ -60,7 +60,7 @@ public VStyleSheet createStyleSheet() { VStyleSheet sheet = new VStyleSheet(); sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD.sub(80))); - sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD), StyleState.HOVERED); + sheet.setKey("label", "font", VFont.create().withColor(VColor.MC_GOLD), VStyleState.HOVERED); sheet.setKey("label", "cursor", VCursorShape.POINTING_HAND); return sheet; diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index fd4a066b..5b16cc37 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -9,7 +9,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VShortcut; import net.snackbag.vera.flag.VAppFlag; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.widget.*; @@ -94,7 +94,7 @@ public void init() { image.move(0, 30); image.onMiddleClick(this::hideCursor); image.onMiddleClickRelease(this::showCursor); - image.setStyle("src", StyleState.HOVERED, "minecraft:textures/block/diamond_block.png"); + image.setStyle("src", VStyleState.HOVERED, "minecraft:textures/block/diamond_block.png"); VDropdown dropdown = new VDropdown(this).alsoAdd(); dropdown.addItem("coolio"); @@ -111,7 +111,7 @@ public void init() { VCheckBox checkbox = new VCheckBox(this).alsoAdd(); checkbox.move(20, 140); - checkbox.setStyle("overlay", StyleState.HOVERED, VColor.white().withOpacity(0.4f)); + checkbox.setStyle("overlay", VStyleState.HOVERED, VColor.white().withOpacity(0.4f)); checkbox.onCheckStateChange((state) -> { if (!state) removeWidget(checkbox); diff --git a/src/main/java/net/snackbag/vera/modifier/VHasFont.java b/src/main/java/net/snackbag/vera/modifier/VHasFont.java index 677c24eb..ce677b85 100644 --- a/src/main/java/net/snackbag/vera/modifier/VHasFont.java +++ b/src/main/java/net/snackbag/vera/modifier/VHasFont.java @@ -2,7 +2,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -11,7 +11,7 @@ default VFont.FontModifier modifyFont() { return modifyFont(null); } - default VFont.FontModifier modifyFont(@Nullable StyleState state) { + default VFont.FontModifier modifyFont(@Nullable VStyleState state) { return getApp().styleSheet.modifyKeyAsFont((VWidget) this, "font", state); } @@ -19,7 +19,7 @@ default VColor.ColorModifier modifyFontColor() { return modifyFontColor(null); } - default VColor.ColorModifier modifyFontColor(@Nullable StyleState state) { + default VColor.ColorModifier modifyFontColor(@Nullable VStyleState state) { return getApp().styleSheet.modifyKeyAsFontColor((VWidget) this, "font", state); } } diff --git a/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java b/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java index 3067b287..bae2a5ef 100644 --- a/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java +++ b/src/main/java/net/snackbag/vera/modifier/VHasPlaceholderFont.java @@ -2,7 +2,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.Nullable; @@ -11,7 +11,7 @@ default VFont.FontModifier modifyPlaceholderFont() { return modifyPlaceholderFont(null); } - default VFont.FontModifier modifyPlaceholderFont(@Nullable StyleState state) { + default VFont.FontModifier modifyPlaceholderFont(@Nullable VStyleState state) { return getApp().styleSheet.modifyKeyAsFont((VWidget) this, "placeholder-font", state); } @@ -19,7 +19,7 @@ default VColor.ColorModifier modifyPlaceholderFontColor() { return modifyPlaceholderFontColor(null); } - default VColor.ColorModifier modifyPlaceholderFontColor(@Nullable StyleState state) { + default VColor.ColorModifier modifyPlaceholderFontColor(@Nullable VStyleState state) { return getApp().styleSheet.modifyKeyAsFontColor((VWidget) this, "placeholder-font", state); } } diff --git a/src/main/java/net/snackbag/vera/style/StyleContainer.java b/src/main/java/net/snackbag/vera/style/StyleContainer.java index 8a3ef522..98523b47 100644 --- a/src/main/java/net/snackbag/vera/style/StyleContainer.java +++ b/src/main/java/net/snackbag/vera/style/StyleContainer.java @@ -12,10 +12,10 @@ * Consists of:
* | Part
* |--- Key
- * |------ StyleState:Object + * |------ VStyleState:Object */ public class StyleContainer { - private final HashMap>> values = new HashMap<>(); + private final HashMap>> values = new HashMap<>(); public StyleContainer() {} @@ -23,7 +23,7 @@ public boolean hasPart(T part) { return values.containsKey(part); } - public HashMap> getPart(T part) { + public HashMap> getPart(T part) { return values.getOrDefault(part, new HashMap<>()); } @@ -31,22 +31,22 @@ public boolean hasKey(T part, String key) { return getPart(part).containsKey(key); } - public HashMap getKey(T part, String key) { + public HashMap getKey(T part, String key) { return getPart(part).getOrDefault(key, new HashMap<>()); } - public boolean hasState(T part, String key, StyleState state) { + public boolean hasState(T part, String key, VStyleState state) { return getKey(part, key).containsKey(state); } - public V getState(T part, String key, StyleState state) { + public V getState(T part, String key, VStyleState state) { return (V) getKey(part, key).get(state); } /** * In this case, exact means that it does not resolve lower states and only - * gives the keys of exactly the given style state. Use {@link #getKeysStacked(Object, StyleState)} + * gives the keys of exactly the given style state. Use {@link #getKeysStacked(Object, VStyleState)} * for deeper state resolve. *

* For example when requesting state HOVERED: @@ -73,16 +73,16 @@ public V getState(T part, String key, StyleState state) { * * * - * @see #getKeysStacked(Object, StyleState) + * @see #getKeysStacked(Object, VStyleState) */ - public Set getKeysExact(T part, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public Set getKeysExact(T part, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; Set buffer = new HashSet<>(); var resolvedPart = getPart(part); // i'm sorry for using var but holy fuck for (String key : resolvedPart.keySet()) { - for (StyleState keyState : resolvedPart.get(key).keySet()) { + for (VStyleState keyState : resolvedPart.get(key).keySet()) { if (keyState != state) continue; buffer.add(key); } @@ -93,7 +93,7 @@ public Set getKeysExact(T part, @Nullable StyleState state) { /** * In this case, stacked means that also all keys from states below the - * given state are returned. Use {@link #getKeysExact(Object, StyleState)} for + * given state are returned. Use {@link #getKeysExact(Object, VStyleState)} for * only the exact keys of a style state. *

* For example when requesting state HOVERED: @@ -120,14 +120,14 @@ public Set getKeysExact(T part, @Nullable StyleState state) { * * * - * @see #getKeysExact(Object, StyleState) + * @see #getKeysExact(Object, VStyleState) */ - public Set getKeysStacked(T part, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public Set getKeysStacked(T part, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; Set buffer = new HashSet<>(); - StyleState next = state; + VStyleState next = state; while (next != null) { buffer.addAll(getKeysExact(part, next)); next = next.fallback; @@ -136,7 +136,7 @@ public Set getKeysStacked(T part, @Nullable StyleState state) { return buffer; } - public void put(T part, String key, StyleState state, Object value) { + public void put(T part, String key, VStyleState state, Object value) { if (!hasPart(part)) values.put(part, new HashMap<>()); if (!hasKey(part, key)) values.get(part).put(key, new HashMap<>()); if (!hasState(part, key, state)) values.get(part).get(key).put(state, new HashMap<>()); @@ -157,7 +157,7 @@ public void moldWith(StyleContainer target) { continue; } - for (StyleState targetState : target.getKey(targetPart, targetKey).keySet()) { + for (VStyleState targetState : target.getKey(targetPart, targetKey).keySet()) { if (!hasState(targetPart, targetKey, targetState)) { values.get(targetPart).get(targetKey).put(targetState, target.getState(targetPart, targetKey, targetState)); continue; diff --git a/src/main/java/net/snackbag/vera/style/VStyleSheet.java b/src/main/java/net/snackbag/vera/style/VStyleSheet.java index a92deb05..1ddc4510 100644 --- a/src/main/java/net/snackbag/vera/style/VStyleSheet.java +++ b/src/main/java/net/snackbag/vera/style/VStyleSheet.java @@ -17,11 +17,11 @@ public class VStyleSheet { private HashMap typeRegistry = new HashMap<>(); public @Nullable T getKey(VWidget widget, String key) { - return getKey(widget, key, StyleState.DEFAULT); + return getKey(widget, key, VStyleState.DEFAULT); } - public @Nullable T getKey(VWidget widget, String key, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public @Nullable T getKey(VWidget widget, String key, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; // if widget contains key if (widgetSpecificStyles.hasKey(widget, key)) { @@ -33,7 +33,7 @@ public class VStyleSheet { } // if class contains key - HashMap> mixed = mixClasses(widget.classes); + HashMap> mixed = mixClasses(widget.classes); Contains: if (mixed.containsKey(key)) { if (!mixed.get(key).containsKey(state)) { @@ -51,8 +51,8 @@ public class VStyleSheet { * In this case, stacked means that also all keys from states below the * given state are returned. */ - public Set getKeysStacked(VWidget widget, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public Set getKeysStacked(VWidget widget, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; Set keys = new HashSet<>(); @@ -73,8 +73,8 @@ public Set getKeysStacked(VWidget widget, @Nullable StyleState state) /** * Note: the resolved keys will return keys from states below the given state */ - public HashMap getResolvedKeys(VWidget widget, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public HashMap getResolvedKeys(VWidget widget, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; Set keys = getKeysStacked(widget, state); HashMap buffer = new HashMap<>(); @@ -94,10 +94,10 @@ public HashMap getResolvedKeys(VWidget widget, @Nullable Styl * 2. If clazz is not registered in {@link VStyleSheet#standardStyles}, recurse into its superclass
* 3. If the specified key is not defined for this class, recurse into its superclass
* 4. Check if the given state is defined:
- *     - If not, and {@link StyleState#fallback} exists, retry with the fallback state on the same class
+ *     - If not, and {@link VStyleState#fallback} exists, retry with the fallback state on the same class
*     - If fallback also fails or is null, recurse into the superclass with the original state */ - public @Nullable T getStandardKey(@Nullable Class clazz, String key, @NotNull StyleState state) { + public @Nullable T getStandardKey(@Nullable Class clazz, String key, @NotNull VStyleState state) { if (clazz == null) return null; // if class isn't registered, attempt super @@ -119,19 +119,19 @@ public HashMap getResolvedKeys(VWidget widget, @Nullable Styl } public void setKey(VWidget widget, String key, Object object) { - setKey(widget, key, object, StyleState.DEFAULT); + setKey(widget, key, object, VStyleState.DEFAULT); } public void setKey(String clazz, String key, Object object) { - setKey(clazz, key, object, StyleState.DEFAULT); + setKey(clazz, key, object, VStyleState.DEFAULT); } public void setKey(Class clazz, String key, Object object) { - setKey(clazz, key, object, StyleState.DEFAULT); + setKey(clazz, key, object, VStyleState.DEFAULT); } - public void setKey(VWidget widget, String key, Object value, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public void setKey(VWidget widget, String key, Object value, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; value = potentiallyUnpackArray(value); StyleValueType res = getReservation(key); @@ -146,8 +146,8 @@ public void setKey(VWidget widget, String key, Object value, @Nullable StyleS widgetSpecificStyles.put(widget, key, state, value); } - public void setKey(String clazz, String key, Object value, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public void setKey(String clazz, String key, Object value, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; value = potentiallyUnpackArray(value); StyleValueType res = getReservation(key); @@ -162,8 +162,8 @@ public void setKey(String clazz, String key, Object value, @Nullable StyleState classStyles.put(clazz, key, state, value); } - public void setKey(Class clazz, String key, Object value, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public void setKey(Class clazz, String key, Object value, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; value = potentiallyUnpackArray(value); StyleValueType res = getReservation(key); @@ -179,36 +179,36 @@ public void setKey(Class clazz, String key, Object value, @Nullable StyleStat } public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key) { - return modifyKeyAsColor(widget, key, StyleState.DEFAULT); + return modifyKeyAsColor(widget, key, VStyleState.DEFAULT); } - public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public VColor.ColorModifier modifyKeyAsColor(VWidget widget, String key, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; - @Nullable StyleState finalState = state; + @Nullable VStyleState finalState = state; return new VColor.ColorModifier(getKey(widget, key, state), color -> setKey(widget, key, color, finalState)); } public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key) { - return modifyKeyAsFont(widget, key, StyleState.DEFAULT); + return modifyKeyAsFont(widget, key, VStyleState.DEFAULT); } - public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public VFont.FontModifier modifyKeyAsFont(VWidget widget, String key, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; - @Nullable StyleState finalState = state; + @Nullable VStyleState finalState = state; return new VFont.FontModifier(getKey(widget, key, state), font -> setKey(widget, key, font, finalState)); } public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key) { - return modifyKeyAsFontColor(widget, key, StyleState.DEFAULT); + return modifyKeyAsFontColor(widget, key, VStyleState.DEFAULT); } - public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key, @Nullable StyleState state) { - if (state == null) state = StyleState.DEFAULT; + public VColor.ColorModifier modifyKeyAsFontColor(VWidget widget, String key, @Nullable VStyleState state) { + if (state == null) state = VStyleState.DEFAULT; // this is cursed - @Nullable StyleState finalState = state; + @Nullable VStyleState finalState = state; return new VColor.ColorModifier( ((VFont) getKey(widget, key, state)).getColor(), color -> modifyKeyAsFont(widget, key, finalState).color(color) @@ -251,11 +251,11 @@ else if (mergedTypeRegistry.get(key) != type) widgetSpecificStyles.moldWith(target.widgetSpecificStyles); } - public HashMap> mixClasses(LinkedHashSet classes) { - final HashMap> values = new HashMap<>(); + public HashMap> mixClasses(LinkedHashSet classes) { + final HashMap> values = new HashMap<>(); for (String clazz : classes) { - HashMap> styles = classStyles.getPart(clazz); + HashMap> styles = classStyles.getPart(clazz); for (String key : styles.keySet()) values.put(key, styles.get(key)); } diff --git a/src/main/java/net/snackbag/vera/style/StyleState.java b/src/main/java/net/snackbag/vera/style/VStyleState.java similarity index 83% rename from src/main/java/net/snackbag/vera/style/StyleState.java rename to src/main/java/net/snackbag/vera/style/VStyleState.java index 7c180e1d..ecd4e429 100644 --- a/src/main/java/net/snackbag/vera/style/StyleState.java +++ b/src/main/java/net/snackbag/vera/style/VStyleState.java @@ -2,7 +2,7 @@ import org.jetbrains.annotations.Nullable; -public enum StyleState { +public enum VStyleState { DEFAULT("default"), HOVERED("hover", DEFAULT), @@ -30,19 +30,19 @@ public enum StyleState { RC_DRAG_RIGHT("rc-drag-right", RC_DRAGGING); public final String identifier; - public final @Nullable StyleState fallback; + public final @Nullable VStyleState fallback; - StyleState(String identifier) { + VStyleState(String identifier) { this(identifier, null); } - StyleState(String identifier, @Nullable StyleState fallback) { + VStyleState(String identifier, @Nullable VStyleState fallback) { this.identifier = identifier; this.fallback = fallback; } - public boolean inherits(StyleState state) { - StyleState next = this; + public boolean inherits(VStyleState state) { + VStyleState next = this; while (next != null) { if (next == state) return true; diff --git a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java index f9daabad..7c957c39 100644 --- a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java @@ -1,7 +1,7 @@ package net.snackbag.vera.style.standard; import net.snackbag.vera.core.VCursorShape; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VCheckBox; @@ -9,7 +9,7 @@ public class CheckBoxStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { - sheet.setKey(VCheckBox.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + sheet.setKey(VCheckBox.class, "cursor", VCursorShape.POINTING_HAND, VStyleState.HOVERED); } @Override diff --git a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java index 8750e490..b584a7af 100644 --- a/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/DropdownStandardStyle.java @@ -4,7 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VDropdown; @@ -13,7 +13,7 @@ public class DropdownStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { sheet.setKey(VDropdown.class, "background-color", VColor.white()); - sheet.setKey(VDropdown.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + sheet.setKey(VDropdown.class, "cursor", VCursorShape.POINTING_HAND, VStyleState.HOVERED); sheet.setKey(VDropdown.class, "font", VFont.create()); sheet.setKey(VDropdown.class, "padding", new V4Int(5, 10)); } diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java index ca4fba13..49a841f0 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -4,7 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VLineInput; @@ -14,7 +14,7 @@ public class LineInputStandardStyle implements VStandardStyle { public void apply(VStyleSheet sheet) { sheet.setKey(VLineInput.class, "select-color", VColor.of(0, 120, 215, 0.2f)); sheet.setKey(VLineInput.class, "background-color", VColor.transparent()); - sheet.setKey(VLineInput.class, "cursor", VCursorShape.TEXT, StyleState.HOVERED); + sheet.setKey(VLineInput.class, "cursor", VCursorShape.TEXT, VStyleState.HOVERED); sheet.setKey(VLineInput.class, "font", VFont.create()); sheet.setKey(VLineInput.class, "placeholder-font", VFont.create().withColor(VColor.black().withOpacity(0.5f))); sheet.setKey(VLineInput.class, "padding", new V4Int(4)); diff --git a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java index 75034653..1a342e54 100644 --- a/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/TabWidgetStandardStyle.java @@ -3,7 +3,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; import net.snackbag.vera.core.VFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.widget.VTabWidget; @@ -14,7 +14,7 @@ public void apply(VStyleSheet sheet) { sheet.setKey(VTabWidget.class, "background-color", VColor.white()); sheet.setKey(VTabWidget.class, "background-color-selected", VColor.white().sub(40)); sheet.setKey(VTabWidget.class, "font", VFont.create()); - sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, StyleState.HOVERED); + sheet.setKey(VTabWidget.class, "cursor", VCursorShape.POINTING_HAND, VStyleState.HOVERED); sheet.setKey(VTabWidget.class, "item-spacing-left", 4); sheet.setKey(VTabWidget.class, "item-spacing-right", 4); diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 8a96a83d..a9e85fbc 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -6,7 +6,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCheckedStateChange; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; public class VCheckBox extends VWidget { private boolean checked; @@ -34,7 +34,7 @@ public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTextu @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); Identifier texture = checked ? getStyle("src-checked", state) : getStyle("src", state); Vera.renderer.drawImage(ctx, 0, 0, width, height, texture); diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index bd4af1db..ae8fb663 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -7,7 +7,7 @@ import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VHasFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -37,7 +37,7 @@ public VDropdown(VeraApp app) { @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VColor backgroundColor = getStyle("background-color", state); VFont font = getStyle("font", state); @@ -123,7 +123,7 @@ public int getEffectiveWidth() { @Override public int getEffectiveHeight() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); V4Int padding = getStyle("padding", state); diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 33e7d82d..0372957d 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -3,7 +3,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; public class VImage extends VWidget { public VImage(Identifier src, int width, int height, VeraApp app) { @@ -19,7 +19,7 @@ public VImage(String src, int width, int height, VeraApp app) { @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); Identifier src = getStyle("src", state); Vera.renderer.drawImage(ctx, 0, 0, width, height, src); diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 37cbf13b..c0ea5cb7 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -5,7 +5,7 @@ import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.modifier.VHasFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; public class VLabel extends VWidget implements VHasFont { private String text; @@ -75,7 +75,7 @@ public VColor.ColorModifier modifyColor(String key) { @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); VColor backgroundColor = getStyle("background-color", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 0f75fa9a..0f305d9a 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -9,7 +9,7 @@ import net.snackbag.vera.event.VCharLimitedEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.modifier.VHasPlaceholderFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -35,7 +35,7 @@ public VLineInput(VeraApp app) { @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); VFont placeholderFont = getStyle("placeholder-font", state); @@ -89,7 +89,7 @@ public void render(RenderContext ctx) { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); int x = getX(); @@ -382,7 +382,7 @@ public void setCursorPos(int cursorPos) { } public VColor getCursorColorSafe() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VColor style = getStyleOrDefault("cursor-color", null, state); VFont font = getStyle("font", state); @@ -392,7 +392,7 @@ public VColor getCursorColorSafe() { @Override public int getEffectiveWidth() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); V4Int padding = getStyle("padding", state); @@ -402,7 +402,7 @@ public int getEffectiveWidth() { @Override public int getEffectiveHeight() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); V4Int padding = getStyle("padding", state); diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 77528100..d5e32402 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -3,7 +3,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; public class VRect extends VWidget { public VRect(VColor color, VeraApp app) { @@ -23,7 +23,7 @@ public VRect(VColor color, int x, int y, int width, int height, VeraApp app) { @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); Vera.renderer.drawRect(ctx, 0, 0, width, height, getStyle("background-color", state)); } diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 8494bdf3..9e0cf49b 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -6,7 +6,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.modifier.VHasFont; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -23,7 +23,7 @@ public VTabWidget(VeraApp app) { @Override public void render(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); VColor defaultBackgroundColor = getStyle("background-color", state); @@ -103,7 +103,7 @@ public boolean isValidTabIndex(@Nullable Integer index) { } public int getHoveredTabIndex(int mouseX) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); int itemSpacingLeft = getStyle("item-spacing-left", state); @@ -201,7 +201,7 @@ public int getEffectiveHeight() { @Override public int getEffectiveWidth() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); VFont font = getStyle("font", state); int itemSpacingLeft = getStyle("item-spacing-left", state); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 19046fa6..46006503 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -7,7 +7,7 @@ import net.snackbag.vera.core.v4.V4Int; import net.snackbag.vera.event.*; import net.snackbag.vera.layout.VLayout; -import net.snackbag.vera.style.StyleState; +import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.animation.AnimationEngine; import net.snackbag.vera.style.animation.CompiledAnimation; import net.snackbag.vera.style.animation.VAnimation; @@ -27,10 +27,10 @@ public abstract class VWidget> extends VElement { private boolean leftClickDown = false; private boolean middleClickDown = false; private boolean rightClickDown = false; - private StyleState handledPrevStyleState = StyleState.DEFAULT; // constantly updates - private StyleState prevStyleState = StyleState.DEFAULT; // updates max once per frame, can be seen as the definite result + private VStyleState handledPrevStyleState = VStyleState.DEFAULT; // constantly updates + private VStyleState prevStyleState = VStyleState.DEFAULT; // updates max once per frame, can be seen as the definite result - private StyleState transitionOrigin = null; + private VStyleState transitionOrigin = null; private boolean isTransitionUnwinding = false; public final LinkedHashSet classes = new LinkedHashSet<>(); @@ -65,7 +65,7 @@ public void setStyle(String key, V... value) { } @SuppressWarnings("unchecked") - public void setStyle(String key, StyleState state, V... value) { + public void setStyle(String key, VStyleState state, V... value) { app.styleSheet.setKey(this, key, value, state); } @@ -73,7 +73,7 @@ public V getStyle(String key) { return animations.animateStyle(key, app.styleSheet.getKey(this, key)); } - public V getStyle(String key, StyleState state) { + public V getStyle(String key, VStyleState state) { return animations.animateStyle(key, app.styleSheet.getKey(this, key, state)); } @@ -82,7 +82,7 @@ public V getStyleOrDefault(String key, V dflt) { return style != null ? style : dflt; } - public V getStyleOrDefault(String key, V dflt, StyleState state) { + public V getStyleOrDefault(String key, V dflt, VStyleState state) { V style = getStyle(key, state); return style != null ? style : dflt; } @@ -138,31 +138,31 @@ public boolean isAnimationActive(String animation) { return animations.isActive(animation); } - public StyleState createStyleState() { + public VStyleState createStyleState() { return createStyleState(true); } - public StyleState createStyleState(boolean respectAnimationLocks) { + public VStyleState createStyleState(boolean respectAnimationLocks) { // Clicks first - if (leftClickDown) return StyleState.LEFT_CLICKED; - else if (middleClickDown) return StyleState.MIDDLE_CLICKED; - else if (rightClickDown) return StyleState.RIGHT_CLICKED; + if (leftClickDown) return VStyleState.LEFT_CLICKED; + else if (middleClickDown) return VStyleState.MIDDLE_CLICKED; + else if (rightClickDown) return VStyleState.RIGHT_CLICKED; else if (DragHandler.isDragging() && DragHandler.target == this) { return switch (DragHandler.button) { - case LEFT -> StyleState.LC_DRAGGING; - case MIDDLE -> StyleState.MC_DRAGGING; - case RIGHT -> StyleState.RC_DRAGGING; + case LEFT -> VStyleState.LC_DRAGGING; + case MIDDLE -> VStyleState.MC_DRAGGING; + case RIGHT -> VStyleState.RC_DRAGGING; }; } // Hover as last, since everything else is hover too - else if (isHovered()) return StyleState.HOVERED; - else return StyleState.DEFAULT; + else if (isHovered()) return VStyleState.HOVERED; + else return VStyleState.DEFAULT; } public RenderContext createRenderContext() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); return new RenderContext( app.getX() + getEffectiveX(), app.getY() + getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), @@ -174,7 +174,7 @@ public RenderContext createRenderContext() { public void renderBorder(RenderContext ctx) { // TODO: [Render Rework] Better border rendering - StyleState state = createStyleState(); + VStyleState state = createStyleState(); V4Color borderColor = getStyle("border-color", state); V4Int borderSize = getStyle("border-size", state); @@ -205,13 +205,13 @@ public void renderBorder(RenderContext ctx) { } public void renderOverlay(RenderContext ctx) { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); Vera.renderer.drawRect(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), getStyle("overlay", state)); } public void beforeRender() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); if (state != prevStyleState) { Integer transitionTime = app.styleSheet.getKey(this, "transition", state); @@ -277,7 +277,7 @@ public void setHasTransparency(boolean hasTransparency) { } public void update() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); app.setCursorShape(getStyle("cursor", state)); } @@ -419,7 +419,7 @@ public void afterBuiltinEvent(String name, Object... args) { } private void updateIfNeeded() { - StyleState state = createStyleState(); + VStyleState state = createStyleState(); if (state != handledPrevStyleState) { update(); handledPrevStyleState = state; From b3571efe7f5d76f06d437ae22da4384bd7e07e5f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 11:08:39 +0100 Subject: [PATCH 594/661] render rect via quads --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 09aafa62..192705fc 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -58,10 +58,11 @@ public void popContext() { } public void drawRect(VWidget.RenderContext ctx, int x, int y, int width, int height, VColor color) { - drawRect( + renderColQuad( x, y, - width, height, - color + x, y + height, + x + width, y + height, + x + width, y, color ); } From 338b1982ba850efe1a822d2000cdfed275344aea Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 11:08:46 +0100 Subject: [PATCH 595/661] render image via quads --- .../java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 192705fc..771ff969 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -78,11 +78,12 @@ public void drawText(VWidget.RenderContext ctx, int x, int y, String text, VFont } public void drawImage(VWidget.RenderContext ctx, int x, int y, int width, int height, Identifier path) { - drawImage( - x, - y, - width, height, - path + renderTexQuad( + ctx.hasTransparency(), path, + x, y, + x, y + height, + x + width, y + height, + x + width, y ); } From 2dd5e4cd3556a5c0d8c867a48f78c03fd37acf87 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 11:35:44 +0100 Subject: [PATCH 596/661] move VRenderContext to separate file and implement clip scissors --- .../snackbag/mcvera/impl/MCVeraRenderer.java | 27 ++--- .../snackbag/vera/util/VRenderContext.java | 98 +++++++++++++++++++ .../net/snackbag/vera/widget/VCheckBox.java | 3 +- .../net/snackbag/vera/widget/VDropdown.java | 3 +- .../java/net/snackbag/vera/widget/VImage.java | 3 +- .../java/net/snackbag/vera/widget/VLabel.java | 3 +- .../net/snackbag/vera/widget/VLineInput.java | 3 +- .../java/net/snackbag/vera/widget/VRect.java | 3 +- .../net/snackbag/vera/widget/VTabWidget.java | 3 +- .../net/snackbag/vera/widget/VWidget.java | 19 ++-- 10 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/util/VRenderContext.java diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 771ff969..1f6af74b 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -17,6 +17,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VAppPositioningFlag; +import net.snackbag.vera.util.VRenderContext; import net.snackbag.vera.widget.VWidget; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; @@ -33,31 +34,31 @@ public class MCVeraRenderer { // Widget rendering // - public void pushContext(VWidget.RenderContext ctx) { + public void pushContext(VRenderContext ctx) { MatrixStack stack = drawContext.getMatrices(); stack.push(); - float wMod = (ctx.width() / 2f) * (ctx.scale() - 1); - float hMod = (ctx.height() / 2f) * (ctx.scale() - 1); + float wMod = (ctx.width / 2f) * (ctx.scale - 1); + float hMod = (ctx.height / 2f) * (ctx.scale - 1); - float xRot = ctx.x() + ctx.width() / 2f; - float yRot = ctx.y() + ctx.height() / 2f; + float xRot = ctx.x + ctx.width / 2f; + float yRot = ctx.y + ctx.height / 2f; // Rotation stack.translate(xRot, yRot, 0f); - stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(ctx.rotation())); + stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(ctx.rotation)); stack.translate(-xRot, -yRot, 0f); // Scale & final positioning) - stack.translate(ctx.x() - wMod, ctx.y() - hMod, 0f); - stack.scale(ctx.scale(), ctx.scale(), 1.0f); + stack.translate(ctx.x - wMod, ctx.y - hMod, 0f); + stack.scale(ctx.scale, ctx.scale, 1.0f); } public void popContext() { drawContext.getMatrices().pop(); } - public void drawRect(VWidget.RenderContext ctx, int x, int y, int width, int height, VColor color) { + public void drawRect(VRenderContext ctx, int x, int y, int width, int height, VColor color) { renderColQuad( x, y, x, y + height, @@ -66,7 +67,7 @@ public void drawRect(VWidget.RenderContext ctx, int x, int y, int width, int hei ); } - public void drawText(VWidget.RenderContext ctx, int x, int y, String text, VFont font) { + public void drawText(VRenderContext ctx, int x, int y, String text, VFont font) { MatrixStack stack = drawContext.getMatrices(); stack.push(); @@ -77,9 +78,9 @@ public void drawText(VWidget.RenderContext ctx, int x, int y, String text, VFont stack.pop(); } - public void drawImage(VWidget.RenderContext ctx, int x, int y, int width, int height, Identifier path) { + public void drawImage(VRenderContext ctx, int x, int y, int width, int height, Identifier path) { renderTexQuad( - ctx.hasTransparency(), path, + ctx.hasTransparency, path, x, y, x, y + height, x + width, y + height, @@ -348,7 +349,7 @@ public void renderApp(VeraApp app) { widget.animations.updateLifetimes(); if (widget.visibilityConditionsPassed()) { - VWidget.RenderContext ctx = widget.createRenderContext(); + VRenderContext ctx = widget.createRenderContext(); pushContext(ctx); widget.render(ctx); diff --git a/src/main/java/net/snackbag/vera/util/VRenderContext.java b/src/main/java/net/snackbag/vera/util/VRenderContext.java new file mode 100644 index 00000000..48a818ee --- /dev/null +++ b/src/main/java/net/snackbag/vera/util/VRenderContext.java @@ -0,0 +1,98 @@ +package net.snackbag.vera.util; + +import net.snackbag.mcvera.impl.MCVeraRenderer; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Objects; + +public class VRenderContext { + public final int x; + public final int y; + public final int width; + public final int height; + public final float rotation; + public final float scale; + public final boolean hasTransparency; + + public VRenderContext( + int x, int y, + int width, int height, + float rotation, float scale, + boolean hasTransparency + ) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.rotation = rotation; + this.scale = scale; + this.hasTransparency = hasTransparency; + } + + private final ArrayList clipStack = new ArrayList<>(); + + public void withClip(int x, int y, int width, int height, Runnable exec) { + pushClip(x, y, width, height); + exec.run(); + popClip(); + } + + public void pushClip(int x, int y, int width, int height) { + clipStack.add(new ClipInstance(x, y, width, height)); + _dcxUpdateScissors(); + } + + public @Nullable ClipInstance peekClip() { + if (clipStack.isEmpty()) return null; + else return clipStack.get(clipStack.size() - 1); + } + + public void popClip() { + if (clipStack.isEmpty()) { + throw new IndexOutOfBoundsException("Cannot pop clip from VRenderContext, because there is nothing in the stack."); + } + + clipStack.remove(clipStack.size() - 1); + _dcxUpdateScissors(); + } + + public void resetClips() { + clipStack.clear(); + _dcxUpdateScissors(); + } + + private void _dcxUpdateScissors() { + ClipInstance instance = peekClip(); + if (instance == null) { + MCVeraRenderer.drawContext.disableScissor(); + return; + } + + MCVeraRenderer.drawContext.enableScissor( + instance.x, instance.y, + instance.x + instance.width, instance.y + instance.height + ); + } + + @Override + public int hashCode() { + return Objects.hash(x, y, width, height, rotation, scale, hasTransparency); + } + + @Override + public String toString() { + return "VRenderContext[" + + "x=" + x + ", " + + "y=" + y + ", " + + "width=" + width + ", " + + "height=" + height + ", " + + "rotation=" + rotation + ", " + + "scale=" + scale + ", " + + "hasTransparency=" + hasTransparency + ']'; + } + + + public record ClipInstance(int x, int y, int width, int height) { + } +} diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index a9e85fbc..1de2ff0b 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -7,6 +7,7 @@ import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCheckedStateChange; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; public class VCheckBox extends VWidget { private boolean checked; @@ -33,7 +34,7 @@ public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTextu } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); Identifier texture = checked ? getStyle("src-checked", state) : getStyle("src", state); diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index ae8fb663..47516053 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -8,6 +8,7 @@ import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -36,7 +37,7 @@ public VDropdown(VeraApp app) { } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); VColor backgroundColor = getStyle("background-color", state); diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 0372957d..7680f357 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -4,6 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; public class VImage extends VWidget { public VImage(Identifier src, int width, int height, VeraApp app) { @@ -18,7 +19,7 @@ public VImage(String src, int width, int height, VeraApp app) { } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); Identifier src = getStyle("src", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index c0ea5cb7..aaaad16c 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -6,6 +6,7 @@ import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; public class VLabel extends VWidget implements VHasFont { private String text; @@ -74,7 +75,7 @@ public VColor.ColorModifier modifyColor(String key) { } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 0f305d9a..aeeed50a 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -10,6 +10,7 @@ import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.modifier.VHasPlaceholderFont; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -34,7 +35,7 @@ public VLineInput(VeraApp app) { } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index d5e32402..5693ab39 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -4,6 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; public class VRect extends VWidget { public VRect(VColor color, VeraApp app) { @@ -22,7 +23,7 @@ public VRect(VColor color, int x, int y, int width, int height, VeraApp app) { } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); Vera.renderer.drawRect(ctx, 0, 0, width, height, getStyle("background-color", state)); diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index 9e0cf49b..d610d9b4 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -7,6 +7,7 @@ import net.snackbag.vera.event.VEvents; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; +import net.snackbag.vera.util.VRenderContext; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -22,7 +23,7 @@ public VTabWidget(VeraApp app) { } @Override - public void render(RenderContext ctx) { + public void render(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 46006503..ae520900 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -1,5 +1,6 @@ package net.snackbag.vera.widget; +import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; @@ -13,6 +14,7 @@ import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.util.DragHandler; +import net.snackbag.vera.util.VRenderContext; import java.nio.file.Path; import java.util.*; @@ -41,7 +43,7 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { this.hasTransparency = false; } - public abstract void render(RenderContext ctx); + public abstract void render(VRenderContext ctx); public int getHitboxX() { return getEffectiveX(); @@ -161,9 +163,9 @@ else if (DragHandler.isDragging() && DragHandler.target == this) { else return VStyleState.DEFAULT; } - public RenderContext createRenderContext() { + public VRenderContext createRenderContext() { VStyleState state = createStyleState(); - return new RenderContext( + return new VRenderContext( app.getX() + getEffectiveX(), app.getY() + getEffectiveY(), getEffectiveWidth(), getEffectiveHeight(), getStyle("rotation", state), getStyle("scale", state), @@ -171,7 +173,7 @@ public RenderContext createRenderContext() { ); } - public void renderBorder(RenderContext ctx) { + public void renderBorder(VRenderContext ctx) { // TODO: [Render Rework] Better border rendering VStyleState state = createStyleState(); @@ -204,7 +206,7 @@ public void renderBorder(RenderContext ctx) { } } - public void renderOverlay(RenderContext ctx) { + public void renderOverlay(VRenderContext ctx) { VStyleState state = createStyleState(); Vera.renderer.drawRect(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), getStyle("overlay", state)); @@ -472,11 +474,4 @@ public T alsoAddTo(VLayout layout) { return (T) this; } - - public record RenderContext( - int x, int y, - int width, int height, - float rotation, float scale, - boolean hasTransparency - ) {} } From ea209d91efe0340d215ff086499e6f2285f4ed61 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 11:57:41 +0100 Subject: [PATCH 597/661] ensure clips are empty --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 1f6af74b..2288208d 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -356,6 +356,7 @@ public void renderApp(VeraApp app) { widget.renderBorder(ctx); widget.renderOverlay(ctx); + ensureClearContext(ctx); popContext(); } @@ -387,4 +388,10 @@ public void renderApps(VAppPositioningFlag flag) { Vera.renderer.renderApp(app); } } + + private void ensureClearContext(VRenderContext ctx) { + if (ctx.peekClip() != null) { + throw new RuntimeException("Unclosed clip stack"); + } + } } From 382d6bb65e3389bd44bf3571a24ec2ead9457a3b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 11:58:07 +0100 Subject: [PATCH 598/661] proper scissoring --- .../snackbag/vera/util/VRenderContext.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/snackbag/vera/util/VRenderContext.java b/src/main/java/net/snackbag/vera/util/VRenderContext.java index 48a818ee..8c4482f3 100644 --- a/src/main/java/net/snackbag/vera/util/VRenderContext.java +++ b/src/main/java/net/snackbag/vera/util/VRenderContext.java @@ -40,7 +40,10 @@ public void withClip(int x, int y, int width, int height, Runnable exec) { public void pushClip(int x, int y, int width, int height) { clipStack.add(new ClipInstance(x, y, width, height)); - _dcxUpdateScissors(); + MCVeraRenderer.drawContext.enableScissor( + this.x + x, this.y + y, + this.x + x + width, this.y + y + height + ); } public @Nullable ClipInstance peekClip() { @@ -54,25 +57,12 @@ public void popClip() { } clipStack.remove(clipStack.size() - 1); - _dcxUpdateScissors(); + MCVeraRenderer.drawContext.disableScissor(); } public void resetClips() { + for (ClipInstance ignored : clipStack) MCVeraRenderer.drawContext.disableScissor(); clipStack.clear(); - _dcxUpdateScissors(); - } - - private void _dcxUpdateScissors() { - ClipInstance instance = peekClip(); - if (instance == null) { - MCVeraRenderer.drawContext.disableScissor(); - return; - } - - MCVeraRenderer.drawContext.enableScissor( - instance.x, instance.y, - instance.x + instance.width, instance.y + instance.height - ); } @Override From e91adfd0733883c483d82e683e3295ffa539e6ee Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 12:08:57 +0100 Subject: [PATCH 599/661] remove weird respectAnimationLocks from VStyleState --- src/main/java/net/snackbag/vera/widget/VWidget.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index ae520900..d4b39d04 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -141,10 +141,6 @@ public boolean isAnimationActive(String animation) { } public VStyleState createStyleState() { - return createStyleState(true); - } - - public VStyleState createStyleState(boolean respectAnimationLocks) { // Clicks first if (leftClickDown) return VStyleState.LEFT_CLICKED; else if (middleClickDown) return VStyleState.MIDDLE_CLICKED; From c7444a9eab72741a16f09553d7c6ee0d65862941 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 13:01:05 +0100 Subject: [PATCH 600/661] mark effective X and Y as deprecated as a note for self --- src/main/java/net/snackbag/vera/VElement.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index b644941d..b4442ccf 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -99,10 +99,12 @@ public int getY() { return layout != null ? layout.posOf(this).y : _y; } + @Deprecated(forRemoval = true) public int getEffectiveX() { return getX(); } + @Deprecated(forRemoval = true) public int getEffectiveY() { return getY(); } From 3b834c9b6dda50d258c1fb144a4249dc7344e450 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 13:01:17 +0100 Subject: [PATCH 601/661] use proper effective methods --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 4 ++-- src/main/java/net/snackbag/vera/layout/VVLayout.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index 7379df62..1aa93627 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -37,8 +37,8 @@ public void rebuild() { protected Vector2i applyAlignment(Vector2i original) { return switch (alignment) { case START -> original; - case CENTER -> new Vector2i(getWidth() / 2 - calculateElementsWidth() / 2 + original.x, original.y); - case END -> new Vector2i(getWidth() - calculateElementsWidth() + original.x, original.y); + case CENTER -> new Vector2i(getEffectiveWidth() / 2 - calculateElementsWidth() / 2 + original.x, original.y); + case END -> new Vector2i(getEffectiveWidth() - calculateElementsWidth() + original.x, original.y); }; } } diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 9dba13bc..d9a6f779 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -37,8 +37,8 @@ public void rebuild() { protected Vector2i applyAlignment(Vector2i original) { return switch (alignment) { case START -> original; - case CENTER -> new Vector2i(original.x, getHeight() / 2 - calculateElementsHeight() / 2 + original.y); - case END -> new Vector2i(original.x, getHeight() - calculateElementsHeight() + original.y); + case CENTER -> new Vector2i(original.x, getEffectiveHeight() / 2 - calculateElementsHeight() / 2 + original.y); + case END -> new Vector2i(original.x, getEffectiveHeight() - calculateElementsHeight() + original.y); }; } } From 45608e2ba99d99ff3b46baf33841c844030047be Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 13:01:39 +0100 Subject: [PATCH 602/661] partly redo rendering --- .../net/snackbag/vera/widget/VLineInput.java | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index aeeed50a..1be7baf0 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -23,6 +23,7 @@ public class VLineInput extends VWidget implements VHasFont, VHasPla private int cursorPos; private TextSelection textSelection; private int maxChars; + private long timeSinceLastInput; public VLineInput(VeraApp app) { super(0, 0, 100, 20, app); @@ -32,12 +33,55 @@ public VLineInput(VeraApp app) { this.cursorPos = 0; this.textSelection = new TextSelection(); this.maxChars = -1; + this.timeSinceLastInput = System.currentTimeMillis(); } @Override public void render(VRenderContext ctx) { VStyleState state = createStyleState(); + VFont font = getStyle("font", state); + VFont placeholderFont = getStyle("placeholder-font", state); + VColor bgColor = getStyle("background-color", state); + VColor textSelectionColor = getStyle("select-color", state); + V4Int padding = getStyle("padding", state); + + // background + Vera.renderer.drawRect(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), bgColor); + + // text selection + int textHeight = Vera.provider.getTextHeight(text, font); + int textX = padding.get3(); + int textY = padding.get1() + height / 2 - textHeight / 2; + + if (!textSelection.isClear()) { + int selStart = Math.min(textSelection.startPos, textSelection.endPos); + String selectedText = getSelectedText(); + + int startX = textX + Vera.provider.getTextWidth(text.substring(0, selStart), font); + int textWidth = Vera.provider.getTextWidth(selectedText, font); + int selTextHeight = Vera.provider.getTextHeight(selectedText, font); + + Vera.renderer.drawRect(ctx, startX, textY, textWidth, selTextHeight, textSelectionColor); + } + + // text + if (text.isEmpty()) Vera.renderer.drawText(ctx, textX, textY, placeholderText, placeholderFont); + else { + Vera.renderer.drawText(ctx, textX, textY, text, font); + } + + // cursor + if (isFocused() && textSelection.isClear() && ((System.currentTimeMillis() - timeSinceLastInput) / 500) % 2 == 0) { + int cursorX = textX + Vera.provider.getTextWidth(text.substring(0, cursorPos), font); + Vera.renderer.drawRect(ctx, cursorX, textY, 1, textHeight, getCursorColorSafe()); + } + } + + /* @Override + public void render(VRenderContext ctx) { + VStyleState state = createStyleState(); + VFont font = getStyle("font", state); VFont placeholderFont = getStyle("placeholder-font", state); VColor backgroundColor = getStyle("background-color", state); @@ -84,7 +128,7 @@ public void render(VRenderContext ctx) { getCursorColorSafe() ); } - } + } */ @Override public void handleBuiltinEvent(String event, Object... args) { @@ -114,9 +158,14 @@ public String getText() { public void setText(String text) { this.text = text; + this.timeSinceLastInput = System.currentTimeMillis(); events.fire(VEvents.LineInput.CHANGE); } + public long getTimeSinceLastInput() { + return timeSinceLastInput; + } + public boolean isSelectingText() { return !textSelection.isClear(); } @@ -328,7 +377,7 @@ private void insertText(String insertion) { String front = text.substring(0, cursorPos); String back = text.substring(cursorPos); - text = front + insertion + back; + setText(front + insertion + back); cursorPos += insertion.length(); events.fire(VEvents.LineInput.CHANGE); } @@ -341,7 +390,7 @@ private void deleteSelectedText() { String front = text.substring(0, start); String back = text.substring(end); - text = front + back; + setText(front + back); cursorPos = start; clearTextSelection(); events.fire(VEvents.LineInput.CHANGE); @@ -360,7 +409,7 @@ private void replaceSelectedText(String replacement) { String front = text.substring(0, start); String back = text.substring(end); - text = front + replacement + back; + setText(front + replacement + back); cursorPos = start + replacement.length(); clearTextSelection(); events.fire(VEvents.LineInput.CHANGE); @@ -394,34 +443,17 @@ public VColor getCursorColorSafe() { @Override public int getEffectiveWidth() { VStyleState state = createStyleState(); - - VFont font = getStyle("font", state); V4Int padding = getStyle("padding", state); - return Math.max(width, Vera.provider.getTextWidth(text, font)) + padding.get3() + padding.get4(); + return width + padding.get3() + padding.get4(); } @Override public int getEffectiveHeight() { VStyleState state = createStyleState(); - - VFont font = getStyle("font", state); V4Int padding = getStyle("padding", state); - return Vera.provider.getTextHeight(text, font) + padding.get1() + padding.get2(); - } - - - @Override - public int getEffectiveX() { - V4Int padding = getStyle("padding", createStyleState()); - return getX() - padding.get4(); - } - - @Override - public int getEffectiveY() { - V4Int padding = getStyle("padding", createStyleState()); - return getY() - padding.get1(); + return height + padding.get1() + padding.get2(); } @Override @@ -440,7 +472,7 @@ public void charTyped(char chr, int modifiers) { String front = text.substring(0, start); String back = text.substring(end); - text = front + chr + back; + setText(front + chr + back); cursorPos = start + 1; clearTextSelection(); events.fire(VEvents.LineInput.CHANGE); @@ -454,7 +486,7 @@ public void charTyped(char chr, int modifiers) { String front = text.substring(0, cursorPos); String back = text.substring(cursorPos); - text = front + chr + back; + setText(front + chr + back); cursorPos += 1; events.fire(VEvents.LineInput.CHANGE); } @@ -533,7 +565,7 @@ private void deleteText(int start, int end) { StringBuilder builder = new StringBuilder(text); builder.delete(start, end); - text = builder.toString(); + setText(builder.toString()); cursorPos = Math.min(start, text.length()); events.fire(VEvents.LineInput.CHANGE); } From 1936e277d26d5eeda83eb802dd0fadb2f7f44f52 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 13:01:52 +0100 Subject: [PATCH 603/661] use effective width --- src/main/java/net/snackbag/vera/widget/VLabel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index aaaad16c..6aa111e6 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -97,7 +97,7 @@ public void render(VRenderContext ctx) { switch (alignment) { case LEFT -> Vera.renderer.drawText(ctx, usualX, usualY, text, font); - case CENTER -> Vera.renderer.drawText(ctx, getWidth() / 2 - Vera.provider.getTextWidth(text, font) / 2, usualY, text, font); + case CENTER -> Vera.renderer.drawText(ctx, getEffectiveWidth() / 2 - Vera.provider.getTextWidth(text, font) / 2, usualY, text, font); case RIGHT -> Vera.renderer.drawText(ctx, getEffectiveWidth() - padding.get4() - Vera.provider.getTextWidth(text, font), usualY, text, font); } } From d2739cc34eb319557648b5c4256433ebda82fc0e Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:01:39 +0100 Subject: [PATCH 604/661] increase maxChars significantly --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 5b16cc37..7d0b95f9 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -40,7 +40,7 @@ public void init() { new VShortcut(this, "leftalt+m", () -> toggleFlag(VAppFlag.REQUIRES_MOUSE)); VLineInput input = new VLineInput(this).alsoAdd(); - input.setMaxChars(15); + input.setMaxChars(30); input.setPlaceholderText("Enter text..."); input.onAddCharLimited(System.out::println); input.onMouseMove((x, y) -> System.out.println("x=" + x + ", y=" + y)); From 8bcb84c79463b4d923c76dc774e41100c8df3abd Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:03:16 +0100 Subject: [PATCH 605/661] smaller --- src/main/java/net/snackbag/vera/widget/VLineInput.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 1be7baf0..fe167d43 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -67,9 +67,7 @@ public void render(VRenderContext ctx) { // text if (text.isEmpty()) Vera.renderer.drawText(ctx, textX, textY, placeholderText, placeholderFont); - else { - Vera.renderer.drawText(ctx, textX, textY, text, font); - } + else Vera.renderer.drawText(ctx, textX, textY, text, font); // cursor if (isFocused() && textSelection.isClear() && ((System.currentTimeMillis() - timeSinceLastInput) / 500) % 2 == 0) { From c441c852dc5c396572c31ec0ca51886233435363 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:03:23 +0100 Subject: [PATCH 606/661] remove old render code --- .../net/snackbag/vera/widget/VLineInput.java | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index fe167d43..30f66a45 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -76,58 +76,6 @@ public void render(VRenderContext ctx) { } } - /* @Override - public void render(VRenderContext ctx) { - VStyleState state = createStyleState(); - - VFont font = getStyle("font", state); - VFont placeholderFont = getStyle("placeholder-font", state); - VColor backgroundColor = getStyle("background-color", state); - VColor textSelectionColor = getStyle("select-color", state); - V4Int padding = getStyle("padding", createStyleState()); - - Vera.renderer.drawRect( - ctx, - 0, - 0, - getEffectiveWidth(), - getEffectiveHeight(), - backgroundColor - ); - - // Render text selection background - if (!textSelection.isClear()) { - int selStart = Math.min(textSelection.startPos, textSelection.endPos); - int selEnd = Math.max(textSelection.startPos, textSelection.endPos); - String beforeSelection = text.substring(0, selStart); - String selectedText = text.substring(selStart, selEnd); - - int selectionX = Vera.provider.getTextWidth(beforeSelection, font); - Vera.renderer.drawRect( - ctx, - selectionX, - padding.get1(), - Vera.provider.getTextWidth(selectedText, font), - Vera.provider.getTextHeight(text, font), - textSelectionColor - ); - } - - if (text.isEmpty()) Vera.renderer.drawText(ctx, padding.get3(), padding.get1(), placeholderText, placeholderFont); - else Vera.renderer.drawText(ctx, padding.get3(), padding.get1(), text, font); - - if (isFocused() && textSelection.isClear() && (System.currentTimeMillis() / 500) % 2 == 0) { - Vera.renderer.drawRect( - ctx, - padding.get3() + Vera.provider.getTextWidth(text.substring(0, cursorPos), font), - padding.get1(), - 1, - Vera.provider.getTextHeight(text, font), - getCursorColorSafe() - ); - } - } */ - @Override public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); From 9b52efddaf6ea26ddbf9ffb086db9d48c4871401 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:03:46 +0100 Subject: [PATCH 607/661] text character selection --- .../net/snackbag/vera/widget/VLineInput.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 30f66a45..05fd310a 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -88,8 +88,25 @@ public void handleBuiltinEvent(String event, Object... args) { if (event.equals(VEvents.Widget.LEFT_CLICK)) { textSelection.clear(); - if (Vera.getMouseX() < x) cursorPos = 0; - else if (Vera.getMouseX() > x + Vera.provider.getTextWidth(text, font)) cursorPos = text.length(); + V4Int padding = getStyle("padding", state); + int textX = x + padding.get3(); + int mouseX = Vera.getMouseX(); + + if (mouseX <= textX) { + setCursorPos(0); + } else { + int bestPos = text.length(); + for (int i = 0; i < text.length(); i++) { + int charMidX = textX + + Vera.provider.getTextWidth(text.substring(0, i), font) + + Vera.provider.getTextWidth(text.substring(i, i + 1), font) / 2; + if (mouseX <= charMidX) { + bestPos = i; + break; + } + } + setCursorPos(bestPos); + } } } From 648c4ad3accf44c1393d58282df644438b31b62a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:04:44 +0100 Subject: [PATCH 608/661] replace manual cursorPos assignment with setCursorPos method invocations --- .../net/snackbag/vera/widget/VLineInput.java | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 05fd310a..8aa40a96 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -271,32 +271,26 @@ else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && cursorPos > 0) { } // Handle word navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isAltDown() && cursorPos > 0) { - cursorPos = Math.max(0, jumpToWordStart(cursorPos)); - events.fire(VEvents.LineInput.CURSOR_MOVE); + setCursorPos(Math.max(0, jumpToWordStart(cursorPos))); events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isAltDown() && cursorPos < text.length()) { - cursorPos = Math.min(text.length(), jumpToWordEnd(cursorPos)); - events.fire(VEvents.LineInput.CURSOR_MOVE); + setCursorPos(Math.min(text.length(), jumpToWordEnd(cursorPos))); events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } // Handle line navigation else if (isDown(GLFW.GLFW_KEY_LEFT) && isCtrlDown()) { - cursorPos = 0; - events.fire(VEvents.LineInput.CURSOR_MOVE); + setCursorPos(0); events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } else if (isDown(GLFW.GLFW_KEY_RIGHT) && isCtrlDown()) { - cursorPos = text.length(); - events.fire(VEvents.LineInput.CURSOR_MOVE); + setCursorPos(text.length()); events.fire(VEvents.LineInput.CURSOR_MOVE_RIGHT); } // Handle character navigation else if (keyCode == GLFW.GLFW_KEY_LEFT && cursorPos > 0) { - cursorPos = Math.max(0, cursorPos - 1); - events.fire(VEvents.LineInput.CURSOR_MOVE); + setCursorPos(Math.max(0, cursorPos - 1)); events.fire(VEvents.LineInput.CURSOR_MOVE_LEFT); } else if (keyCode == GLFW.GLFW_KEY_RIGHT && cursorPos < text.length()) { - cursorPos = Math.min(text.length(), cursorPos + 1); - events.fire(VEvents.LineInput.CURSOR_MOVE); + setCursorPos(Math.min(text.length(), cursorPos + 1)); events.fire(VEvents.LineInput.CURSOR_MOVE_RIGHT); } @@ -327,9 +321,8 @@ private void handleSelectionKeyPress(int keyCode) { } } - cursorPos = newPos; + setCursorPos(newPos); textSelection.endPos = newPos; - events.fire(VEvents.LineInput.CURSOR_MOVE); } private void insertText(String insertion) { @@ -341,8 +334,7 @@ private void insertText(String insertion) { String front = text.substring(0, cursorPos); String back = text.substring(cursorPos); setText(front + insertion + back); - cursorPos += insertion.length(); - events.fire(VEvents.LineInput.CHANGE); + setCursorPos(cursorPos + insertion.length()); } private void deleteSelectedText() { @@ -354,7 +346,7 @@ private void deleteSelectedText() { String front = text.substring(0, start); String back = text.substring(end); setText(front + back); - cursorPos = start; + setCursorPos(start); clearTextSelection(); events.fire(VEvents.LineInput.CHANGE); } @@ -373,7 +365,7 @@ private void replaceSelectedText(String replacement) { String front = text.substring(0, start); String back = text.substring(end); setText(front + replacement + back); - cursorPos = start + replacement.length(); + setCursorPos(start + replacement.length()); clearTextSelection(); events.fire(VEvents.LineInput.CHANGE); } @@ -392,6 +384,7 @@ public int getCursorPos() { public void setCursorPos(int cursorPos) { this.cursorPos = cursorPos; + events.fire(VEvents.LineInput.CURSOR_MOVE); } public VColor getCursorColorSafe() { @@ -436,7 +429,7 @@ public void charTyped(char chr, int modifiers) { String back = text.substring(end); setText(front + chr + back); - cursorPos = start + 1; + setCursorPos(start + 1); clearTextSelection(); events.fire(VEvents.LineInput.CHANGE); } else { @@ -450,8 +443,7 @@ public void charTyped(char chr, int modifiers) { String back = text.substring(cursorPos); setText(front + chr + back); - cursorPos += 1; - events.fire(VEvents.LineInput.CHANGE); + setCursorPos(cursorPos + 1); } } super.charTyped(chr, modifiers); @@ -518,7 +510,7 @@ private int jumpToWordEnd(int position) { public void selectAll() { textSelection.startPos = 0; textSelection.endPos = text.length(); - cursorPos = text.length(); + setCursorPos(text.length()); } private void deleteText(int start, int end) { @@ -529,8 +521,7 @@ private void deleteText(int start, int end) { StringBuilder builder = new StringBuilder(text); builder.delete(start, end); setText(builder.toString()); - cursorPos = Math.min(start, text.length()); - events.fire(VEvents.LineInput.CHANGE); + setCursorPos(Math.min(start, text.length())); } public static class TextSelection { From fb6cebd22088f7d19233948efbf0f650cca5a9ee Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:04:53 +0100 Subject: [PATCH 609/661] remove change events --- src/main/java/net/snackbag/vera/widget/VLineInput.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 8aa40a96..bd911154 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -348,7 +348,6 @@ private void deleteSelectedText() { setText(front + back); setCursorPos(start); clearTextSelection(); - events.fire(VEvents.LineInput.CHANGE); } private void replaceSelectedText(String replacement) { @@ -367,7 +366,6 @@ private void replaceSelectedText(String replacement) { setText(front + replacement + back); setCursorPos(start + replacement.length()); clearTextSelection(); - events.fire(VEvents.LineInput.CHANGE); } @@ -431,7 +429,6 @@ public void charTyped(char chr, int modifiers) { setText(front + chr + back); setCursorPos(start + 1); clearTextSelection(); - events.fire(VEvents.LineInput.CHANGE); } else { // Normal character insertion if (maxChars > -1 && text.length() >= maxChars) { From 544eed832b854b1cce199b6ef4aed5fc5836fd9b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:05:09 +0100 Subject: [PATCH 610/661] remove all usages of effectiveX and effectiveY --- src/main/java/net/snackbag/vera/widget/VWidget.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index d4b39d04..5cd4e4a0 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -46,11 +46,11 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { public abstract void render(VRenderContext ctx); public int getHitboxX() { - return getEffectiveX(); + return getX(); } public int getHitboxY() { - return getEffectiveY(); + return getY(); } public int getHitboxWidth() { @@ -162,7 +162,7 @@ else if (DragHandler.isDragging() && DragHandler.target == this) { public VRenderContext createRenderContext() { VStyleState state = createStyleState(); return new VRenderContext( - app.getX() + getEffectiveX(), app.getY() + getEffectiveY(), + app.getX() + getX(), app.getY() + getY(), getEffectiveWidth(), getEffectiveHeight(), getStyle("rotation", state), getStyle("scale", state), hasTransparency From b3bc2b06276c80d9f01ebdaa0e2b4f5bdf94caea Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:18:06 +0100 Subject: [PATCH 611/661] move cursor getting to new getCursorPosAtX method --- .../net/snackbag/vera/widget/VLineInput.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index bd911154..cc35d6f6 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -80,33 +80,9 @@ public void render(VRenderContext ctx) { public void handleBuiltinEvent(String event, Object... args) { super.handleBuiltinEvent(event, args); - VStyleState state = createStyleState(); - VFont font = getStyle("font", state); - - int x = getX(); - if (event.equals(VEvents.Widget.LEFT_CLICK)) { textSelection.clear(); - - V4Int padding = getStyle("padding", state); - int textX = x + padding.get3(); - int mouseX = Vera.getMouseX(); - - if (mouseX <= textX) { - setCursorPos(0); - } else { - int bestPos = text.length(); - for (int i = 0; i < text.length(); i++) { - int charMidX = textX - + Vera.provider.getTextWidth(text.substring(0, i), font) - + Vera.provider.getTextWidth(text.substring(i, i + 1), font) / 2; - if (mouseX <= charMidX) { - bestPos = i; - break; - } - } - setCursorPos(bestPos); - } + setCursorPos(getCursorPosAtX(getRelativeMouseX())); } } @@ -385,6 +361,30 @@ public void setCursorPos(int cursorPos) { events.fire(VEvents.LineInput.CURSOR_MOVE); } + public int getCursorPosAtX(int x) { + VStyleState state = createStyleState(); + VFont font = getStyle("font", state); + V4Int padding = getStyle("padding", state); + + int textX = padding.get3(); + + if (x <= textX) { + return 0; + } else { + int bestPos = text.length(); + for (int i = 0; i < text.length(); i++) { + int charMidX = textX + + Vera.provider.getTextWidth(text.substring(0, i), font) + + Vera.provider.getTextWidth(text.substring(i, i + 1), font) / 2; + if (x <= charMidX) { + bestPos = i; + break; + } + } + return bestPos; + } + } + public VColor getCursorColorSafe() { VStyleState state = createStyleState(); From 2bb869c37b79028fbc584febc4fde998743a469b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:18:15 +0100 Subject: [PATCH 612/661] ensure cursor is visible when changing its position --- src/main/java/net/snackbag/vera/widget/VLineInput.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index cc35d6f6..eb6f46ad 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -358,6 +358,7 @@ public int getCursorPos() { public void setCursorPos(int cursorPos) { this.cursorPos = cursorPos; + this.timeSinceLastInput = System.currentTimeMillis(); events.fire(VEvents.LineInput.CURSOR_MOVE); } From 66841270068695811f6d5b8a4fea91ed3a84e8f6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:18:25 +0100 Subject: [PATCH 613/661] stop using getEffectiveX --- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index d610d9b4..feb66782 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -110,7 +110,7 @@ public int getHoveredTabIndex(int mouseX) { int itemSpacingLeft = getStyle("item-spacing-left", state); int itemSpacingRight = getStyle("item-spacing-right", state); - int relativeX = mouseX - getEffectiveX(); + int relativeX = mouseX - getX(); int currentX = 0; int index = 0; From 6d5a09751f22fddf269e258b149266ebdde7f3e6 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 14:18:40 +0100 Subject: [PATCH 614/661] add methods to get the relative mouse position --- src/main/java/net/snackbag/vera/VElement.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index b4442ccf..389de601 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -109,6 +109,14 @@ public int getEffectiveY() { return getY(); } + public int getRelativeMouseX() { + return Vera.getMouseX() - getX() - app.getX(); + } + + public int getRelativeMouseY() { + return Vera.getMouseY() - getY() - app.getY(); + } + public void move(int both) { move(both, both); } From 75b526b522689f83180179d1b4a50be083100963 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 15:38:25 +0100 Subject: [PATCH 615/661] render text up until the end of the widget --- src/main/java/net/snackbag/vera/widget/VLineInput.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index eb6f46ad..ad5aeda6 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -51,7 +51,8 @@ public void render(VRenderContext ctx) { // text selection int textHeight = Vera.provider.getTextHeight(text, font); - int textX = padding.get3(); + int textWidth = Vera.provider.getTextWidth(text, font); + int textX = textWidth < width ? padding.get3() : padding.get3() - textWidth + width; int textY = padding.get1() + height / 2 - textHeight / 2; if (!textSelection.isClear()) { @@ -59,10 +60,10 @@ public void render(VRenderContext ctx) { String selectedText = getSelectedText(); int startX = textX + Vera.provider.getTextWidth(text.substring(0, selStart), font); - int textWidth = Vera.provider.getTextWidth(selectedText, font); + int selTextWidth = Vera.provider.getTextWidth(selectedText, font); int selTextHeight = Vera.provider.getTextHeight(selectedText, font); - Vera.renderer.drawRect(ctx, startX, textY, textWidth, selTextHeight, textSelectionColor); + Vera.renderer.drawRect(ctx, startX, textY, selTextWidth, selTextHeight, textSelectionColor); } // text From 3136829f49301f757e6e21389e199f514e2837a2 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 18:19:44 +0100 Subject: [PATCH 616/661] [Render Rework] Redo full rendering and implement proper text viewport handling --- .../net/snackbag/vera/widget/VLineInput.java | 114 +++++++++++++++--- 1 file changed, 95 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index ad5aeda6..ef0e5a35 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -2,6 +2,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.InputUtil; +import net.minecraft.util.math.MathHelper; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; import net.snackbag.vera.core.v4.V4Int; @@ -15,7 +16,6 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -// TODO: [Render Rework] Rewrite rendering from scratch public class VLineInput extends VWidget implements VHasFont, VHasPlaceholderFont { private String text; private String placeholderText; @@ -24,6 +24,7 @@ public class VLineInput extends VWidget implements VHasFont, VHasPla private TextSelection textSelection; private int maxChars; private long timeSinceLastInput; + private int textViewport = 0; public VLineInput(VeraApp app) { super(0, 0, 100, 20, app); @@ -45,34 +46,51 @@ public void render(VRenderContext ctx) { VColor bgColor = getStyle("background-color", state); VColor textSelectionColor = getStyle("select-color", state); V4Int padding = getStyle("padding", state); + String rText = getTextInViewport(); // background Vera.renderer.drawRect(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), bgColor); // text selection int textHeight = Vera.provider.getTextHeight(text, font); - int textWidth = Vera.provider.getTextWidth(text, font); - int textX = textWidth < width ? padding.get3() : padding.get3() - textWidth + width; + int textWidth = Vera.provider.getTextWidth(rText, font); + int textX = Vera.provider.getTextWidth(text, font) < width + ? padding.get3() + : padding.get3() - textWidth + width; int textY = padding.get1() + height / 2 - textHeight / 2; if (!textSelection.isClear()) { int selStart = Math.min(textSelection.startPos, textSelection.endPos); - String selectedText = getSelectedText(); + int selEnd = Math.max(textSelection.startPos, textSelection.endPos); + + // clamp selection to the visible viewport + int visStart = Math.max(selStart, textViewport); + int visEnd = Math.min(selEnd, getTextViewportEnd()); - int startX = textX + Vera.provider.getTextWidth(text.substring(0, selStart), font); - int selTextWidth = Vera.provider.getTextWidth(selectedText, font); - int selTextHeight = Vera.provider.getTextHeight(selectedText, font); + if (visStart < visEnd) { + String beforeSel = rText.substring(0, visStart - textViewport); + String selInView = rText.substring(visStart - textViewport, visEnd - textViewport); - Vera.renderer.drawRect(ctx, startX, textY, selTextWidth, selTextHeight, textSelectionColor); + int startX = textX + Vera.provider.getTextWidth(beforeSel, font); + int selTextWidth = Vera.provider.getTextWidth(selInView, font); + int selTextHeight = Vera.provider.getTextHeight(selInView, font); + + Vera.renderer.drawRect(ctx, startX, textY, selTextWidth, selTextHeight, textSelectionColor); + } } // text if (text.isEmpty()) Vera.renderer.drawText(ctx, textX, textY, placeholderText, placeholderFont); - else Vera.renderer.drawText(ctx, textX, textY, text, font); + else Vera.renderer.drawText(ctx, textX, textY, rText, font); // cursor if (isFocused() && textSelection.isClear() && ((System.currentTimeMillis() - timeSinceLastInput) / 500) % 2 == 0) { - int cursorX = textX + Vera.provider.getTextWidth(text.substring(0, cursorPos), font); + int cursorX = textX + Vera.provider.getTextWidth( + rText.substring(0, Math.min( + cursorPos - textViewport, + rText.length() + )), font + ); Vera.renderer.drawRect(ctx, cursorX, textY, 1, textHeight, getCursorColorSafe()); } } @@ -83,7 +101,7 @@ public void handleBuiltinEvent(String event, Object... args) { if (event.equals(VEvents.Widget.LEFT_CLICK)) { textSelection.clear(); - setCursorPos(getCursorPosAtX(getRelativeMouseX())); + setCursorPos(getCharPosAtX(getRelativeMouseX())); } } @@ -102,6 +120,10 @@ public void setText(String text) { events.fire(VEvents.LineInput.CHANGE); } + private String getTextInViewport() { + return text.substring(textViewport, getTextViewportEnd()); + } + public long getTimeSinceLastInput() { return timeSinceLastInput; } @@ -324,6 +346,7 @@ private void deleteSelectedText() { String back = text.substring(end); setText(front + back); setCursorPos(start); +// setTextViewport(textViewport - (end - start)); clearTextSelection(); } @@ -342,6 +365,7 @@ private void replaceSelectedText(String replacement) { String back = text.substring(end); setText(front + replacement + back); setCursorPos(start + replacement.length()); +// setTextViewport(textViewport - (end - start)); clearTextSelection(); } @@ -353,6 +377,52 @@ private String getSelectedText() { return text.substring(start, end); } + public void setTextViewport(int textViewport) { + this.textViewport = MathHelper.clamp(textViewport, 0, text.length()); + } + + public int getTextViewportBegin() { + return textViewport; + } + + private int getTextViewportEnd() { + VStyleState state = createStyleState(); + VFont font = getStyle("font", state); + + StringBuilder buf = new StringBuilder(); + for (int i = textViewport; i < text.length(); i++) { + buf.append(text.charAt(i)); + if (Vera.provider.getTextWidth(buf.toString(), font) <= width) continue; + return i; + } + + return text.length(); + } + + private void updateTextViewport() { + textViewport = MathHelper.clamp(textViewport, 0, text.length()); + + // Cursor is before the viewport: snap left + if (cursorPos < textViewport) { + textViewport = cursorPos; + } + + // cursor is past the viewport end: advance right + int end; + while ((end = getTextViewportEnd()) <= cursorPos && end < text.length()) { + textViewport++; + } + + // if the end of the text is visible, try scrolling back left + while (textViewport > 0 && getTextViewportEnd() == text.length()) { + textViewport--; + } + // if that last decrement caused overflow that hides the cursor, undo it + if (getTextViewportEnd() < text.length() && cursorPos >= getTextViewportEnd()) { + textViewport++; + } + } + public int getCursorPos() { return cursorPos; } @@ -361,29 +431,34 @@ public void setCursorPos(int cursorPos) { this.cursorPos = cursorPos; this.timeSinceLastInput = System.currentTimeMillis(); events.fire(VEvents.LineInput.CURSOR_MOVE); + updateTextViewport(); } - public int getCursorPosAtX(int x) { + public int getCharPosAtX(int x) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); V4Int padding = getStyle("padding", state); - int textX = padding.get3(); + String rText = getTextInViewport(); + int rTextWidth = Vera.provider.getTextWidth(rText, font); + int textX = Vera.provider.getTextWidth(text, font) < width + ? padding.get3() + : padding.get3() - rTextWidth + width; if (x <= textX) { - return 0; + return textViewport; } else { - int bestPos = text.length(); - for (int i = 0; i < text.length(); i++) { + int bestPos = rText.length(); + for (int i = 0; i < rText.length(); i++) { int charMidX = textX - + Vera.provider.getTextWidth(text.substring(0, i), font) - + Vera.provider.getTextWidth(text.substring(i, i + 1), font) / 2; + + Vera.provider.getTextWidth(rText.substring(0, i), font) + + Vera.provider.getTextWidth(rText.substring(i, i + 1), font) / 2; if (x <= charMidX) { bestPos = i; break; } } - return bestPos; + return textViewport + bestPos; } } @@ -521,6 +596,7 @@ private void deleteText(int start, int end) { builder.delete(start, end); setText(builder.toString()); setCursorPos(Math.min(start, text.length())); +// setTextViewport(textViewport - (end - start)); } public static class TextSelection { From e8f2854462e65dbe5372a22f31ac1056290c6e94 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 19:07:24 +0100 Subject: [PATCH 617/661] resolve TODO: background won't be rendered if the bg color is transparent --- .../java/net/snackbag/vera/widget/VLabel.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 6aa111e6..6b6eb6fb 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -82,15 +82,16 @@ public void render(VRenderContext ctx) { VColor backgroundColor = getStyle("background-color", state); V4Int padding = getStyle("padding", state); - // TODO: render optimization; if background color is transparent, skip - Vera.renderer.drawRect( - ctx, - 0, - 0, - getEffectiveWidth(), - getEffectiveHeight(), - backgroundColor - ); + if (!backgroundColor.isTransparent()) { + Vera.renderer.drawRect( + ctx, + 0, + 0, + getEffectiveWidth(), + getEffectiveHeight(), + backgroundColor + ); + } int usualX = padding.get3(); int usualY = padding.get1(); From 6e19dd012b078ff26eb7ec89db211481e14b0f6c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 19:09:33 +0100 Subject: [PATCH 618/661] move VRenderContext from util to core --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- .../java/net/snackbag/vera/{util => core}/VRenderContext.java | 2 +- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 2 +- src/main/java/net/snackbag/vera/widget/VDropdown.java | 2 +- src/main/java/net/snackbag/vera/widget/VImage.java | 2 +- src/main/java/net/snackbag/vera/widget/VLabel.java | 2 +- src/main/java/net/snackbag/vera/widget/VLineInput.java | 2 +- src/main/java/net/snackbag/vera/widget/VRect.java | 2 +- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 2 +- src/main/java/net/snackbag/vera/widget/VWidget.java | 3 +-- 10 files changed, 10 insertions(+), 11 deletions(-) rename src/main/java/net/snackbag/vera/{util => core}/VRenderContext.java (98%) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 2288208d..6569ec9e 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -17,7 +17,7 @@ import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VAppPositioningFlag; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; import net.snackbag.vera.widget.VWidget; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; diff --git a/src/main/java/net/snackbag/vera/util/VRenderContext.java b/src/main/java/net/snackbag/vera/core/VRenderContext.java similarity index 98% rename from src/main/java/net/snackbag/vera/util/VRenderContext.java rename to src/main/java/net/snackbag/vera/core/VRenderContext.java index 8c4482f3..eac608b5 100644 --- a/src/main/java/net/snackbag/vera/util/VRenderContext.java +++ b/src/main/java/net/snackbag/vera/core/VRenderContext.java @@ -1,4 +1,4 @@ -package net.snackbag.vera.util; +package net.snackbag.vera.core; import net.snackbag.mcvera.impl.MCVeraRenderer; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 1de2ff0b..f923152d 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -7,7 +7,7 @@ import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCheckedStateChange; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; public class VCheckBox extends VWidget { private boolean checked; diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 47516053..167dc057 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -8,7 +8,7 @@ import net.snackbag.vera.event.VItemSwitchEvent; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java index 7680f357..1773829d 100644 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ b/src/main/java/net/snackbag/vera/widget/VImage.java @@ -4,7 +4,7 @@ import net.snackbag.vera.Vera; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; public class VImage extends VWidget { public VImage(Identifier src, int width, int height, VeraApp app) { diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 6b6eb6fb..cbf2c422 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -6,7 +6,7 @@ import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; public class VLabel extends VWidget implements VHasFont { private String text; diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index ef0e5a35..b50dc0fb 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -11,7 +11,7 @@ import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.modifier.VHasPlaceholderFont; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 5693ab39..e13ec4a5 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -4,7 +4,7 @@ import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; public class VRect extends VWidget { public VRect(VColor color, VeraApp app) { diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index feb66782..f1b4f036 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -7,7 +7,7 @@ import net.snackbag.vera.event.VEvents; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; import org.jetbrains.annotations.Nullable; import java.util.*; diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 5cd4e4a0..2c90854c 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -1,6 +1,5 @@ package net.snackbag.vera.widget; -import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; import net.snackbag.vera.core.*; @@ -14,7 +13,7 @@ import net.snackbag.vera.style.animation.VAnimation; import net.snackbag.vera.style.animation.easing.VEasing; import net.snackbag.vera.util.DragHandler; -import net.snackbag.vera.util.VRenderContext; +import net.snackbag.vera.core.VRenderContext; import java.nio.file.Path; import java.util.*; From 82815080e85114738280953d7a2caa22f5a61744 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:14:54 +0100 Subject: [PATCH 619/661] add VFill to prepare for merge of colors and images --- .../java/net/snackbag/vera/core/VFill.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/core/VFill.java diff --git a/src/main/java/net/snackbag/vera/core/VFill.java b/src/main/java/net/snackbag/vera/core/VFill.java new file mode 100644 index 00000000..6044e8f8 --- /dev/null +++ b/src/main/java/net/snackbag/vera/core/VFill.java @@ -0,0 +1,20 @@ +package net.snackbag.vera.core; + +import net.snackbag.vera.style.animation.easing.VEasing; + +public interface VFill { + static VFill empty() { + return new VFill() { + @Override + public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) {} + + @Override + public VFill ease(VEasing easing, VFill target, float delta) { + return target; + } + }; + } + + void renderQuad(VRenderContext ctx, int x, int y, int width, int height); + VFill ease(VEasing easing, VFill target, float delta); +} From a7d2b848bdf5728c2f2f47556e36495800f8e36b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:15:39 +0100 Subject: [PATCH 620/661] implement color and image fill --- .../java/net/snackbag/vera/core/VColor.java | 15 ++++++- .../java/net/snackbag/vera/core/VImage.java | 45 +++++++++++++++++++ .../snackbag/vera/style/StyleValueType.java | 34 ++++++++++---- 3 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/core/VImage.java diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 85d77d06..2e2953cc 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -1,11 +1,12 @@ package net.snackbag.vera.core; +import net.snackbag.vera.Vera; import net.snackbag.vera.style.animation.easing.VEasing; import org.jetbrains.annotations.ApiStatus; import java.util.function.Consumer; -public class VColor { +public class VColor implements VFill { public static final VColor MC_BLACK = VColor.black(); public static final VColor MC_DARK_BLUE = VColor.of(0, 0, 170); public static final VColor MC_DARK_GREEN = VColor.of(0, 170, 0); @@ -183,7 +184,12 @@ public VColor sub(int red, int green, int blue) { return new VColor(Math.max(this.red - red, 0), Math.max(this.green - green, 0), Math.max(this.blue - blue, 0)); } - public VColor ease(VEasing easing, VColor target, float delta) { + @Override + public VFill ease(VEasing easing, VFill targetRaw, float delta) { + if (!(targetRaw instanceof VColor target)) { + throw new ClassCastException("Cannot ease two different types of VFill to color."); + } + return new VColor( easing.apply(red, target.red, delta), easing.apply(green, target.green, delta), @@ -204,6 +210,11 @@ public static VColor black() { return new VColor(0, 0, 0, 1); } + @Override + public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) { + Vera.renderer.drawRect(ctx, x, y, width, height, this); + } + public static class ColorModifier { private VColor color; private final Consumer colorUpdater; diff --git a/src/main/java/net/snackbag/vera/core/VImage.java b/src/main/java/net/snackbag/vera/core/VImage.java new file mode 100644 index 00000000..9b79f818 --- /dev/null +++ b/src/main/java/net/snackbag/vera/core/VImage.java @@ -0,0 +1,45 @@ +package net.snackbag.vera.core; + +import net.minecraft.util.Identifier; +import net.snackbag.vera.Vera; +import net.snackbag.vera.style.StyleValueType; +import net.snackbag.vera.style.animation.easing.VEasing; + +public class VImage implements VFill { + public final Identifier src; + public final VColor tint; + + public VImage(String src, VColor tint) { + this(new Identifier(src), tint); + } + + public VImage(String src) { + this(new Identifier(src)); + } + + public VImage(Identifier src, VColor tint) { + this.src = src; + this.tint = tint; + } + + public VImage(Identifier src) { + this(src, VColor.white()); + } + + @Override + public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) { + Vera.renderer.drawImage(ctx, x, y, width, height, src); + } + + @Override + public VFill ease(VEasing easing, VFill targetRaw, float delta) { + if (!(targetRaw instanceof VImage target)) { + throw new ClassCastException("Cannot ease two different types of VFill to image."); + } + + return new VImage( + (Identifier) StyleValueType.IDENTIFIER.animationTransition.apply(src, target.src, easing, delta), + (VColor) tint.ease(easing, target.tint, delta) + ); + } +} diff --git a/src/main/java/net/snackbag/vera/style/StyleValueType.java b/src/main/java/net/snackbag/vera/style/StyleValueType.java index 206679d1..02bfe8f7 100644 --- a/src/main/java/net/snackbag/vera/style/StyleValueType.java +++ b/src/main/java/net/snackbag/vera/style/StyleValueType.java @@ -16,9 +16,11 @@ public enum StyleValueType { INT(0, (from, to, easing, delta) -> easing.apply(from, to, delta)), FLOAT(0.0F, (from, to, easing, delta) -> easing.apply(from, to, delta)), - COLOR(VColor.black(), (from, to, easing, delta) -> from.ease(easing, to, delta)), + FILL(VFill.empty(), (from, to, easing, delta) -> + from.ease(easing, to, delta)), + COLOR(VColor.black(), (from, to, easing, delta) -> (VColor) from.ease(easing, to, delta)), FONT(VFont.create(), (from, to, easing, delta) -> - VFont.create().withColor(from.getColor().ease(easing, to.getColor(), delta)) + VFont.create().withColor((VColor) from.getColor().ease(easing, to.getColor(), delta)) .withSize(easing.apply(from.getSize(), to.getSize(), delta)) .withName(delta > 0.5 ? to.getName() : from.getName())), CURSOR(VCursorShape.DEFAULT, (f, t, e, d) -> d > 0.5 ? t : f), @@ -31,12 +33,14 @@ public enum StyleValueType { easing.apply(from.get4(), to.get4(), delta) )), V4COLOR(new V4Color(VColor.black()), (from, to, easing, delta) -> new V4Color( - from.get1().ease(easing, to.get1(), delta), - from.get2().ease(easing, to.get2(), delta), - from.get3().ease(easing, to.get3(), delta), - from.get4().ease(easing, to.get4(), delta) + (VColor) from.get1().ease(easing, to.get1(), delta), + (VColor) from.get2().ease(easing, to.get2(), delta), + (VColor) from.get3().ease(easing, to.get3(), delta), + (VColor) from.get4().ease(easing, to.get4(), delta) )); + private static final String ID_REGEX = "^[\\w-./]*:[\\w-./]*$"; + public final Object standard; public final EaseContext animationTransition; @@ -47,20 +51,28 @@ StyleValueType(T standard, EaseContext animationTransition) { public static StyleValueType get(Object val, @Nullable StyleValueType bias) { if (val instanceof String s) { - if (bias == IDENTIFIER && s.matches("^[\\w-./]*:[\\w-./]*$")) return IDENTIFIER; + if (bias == IDENTIFIER && s.matches(ID_REGEX)) return IDENTIFIER; else if (bias == CURSOR && EnumUtils.getEnumIgnoreCase(VCursorShape.class, s) != null) return CURSOR; else if (bias == EASING && VEasings.getIgnoreCase(s) != null) return EASING; + else if (bias == FILL && s.matches(ID_REGEX)) return FILL; return STRING; } else if (val instanceof V4Color || (bias == V4COLOR && (val instanceof VColor[] || val instanceof VColor))) return V4COLOR; else if (val instanceof V4Int || (bias == V4INT && (val instanceof int[] || val instanceof Integer[] || val instanceof Integer))) return V4INT; - else if (val instanceof Identifier) return IDENTIFIER; + else if (val instanceof Identifier) { + if (bias == FILL) return FILL; + return IDENTIFIER; + } else if (val instanceof VCursorShape) return CURSOR; else if (val instanceof VEasing) return EASING; else if (val instanceof Integer) return INT; else if (val instanceof Float || val instanceof Double) return FLOAT; - else if (val instanceof VColor) return COLOR; + else if (val instanceof VColor) { + if (bias == FILL) return FILL; + return COLOR; + } + else if (val instanceof VImage) return FILL; else if (val instanceof VFont) return FONT; else throw new RuntimeException("%s isn't a valid style type".formatted(val.getClass().getName())); } @@ -70,8 +82,11 @@ public static Object convert(Object value, StyleValueType to) { if (to == IDENTIFIER) return new Identifier(v); else if (to == CURSOR) return EnumUtils.getEnumIgnoreCase(VCursorShape.class, v); else if (to == EASING) return VEasings.getIgnoreCase(v); + else if (to == FILL) return new VImage(new Identifier(v)); } + else if (value instanceof Identifier i && to == FILL) return new VImage(i); + else if (value instanceof int[] || value instanceof Integer[]) { Integer[] v = (Integer[]) value; @@ -97,6 +112,7 @@ else if (value instanceof VColor[] v) { } else if (value instanceof VColor v && to == V4COLOR) return new V4Color(v); + else if (value instanceof VFill && to == FILL) return value; else if (to == FLOAT && value instanceof Double v) return v.floatValue(); return value; From 0ed5f8247f6fa77226dc5747a446c78b716e2c18 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:16:07 +0100 Subject: [PATCH 621/661] tests for fills --- .../net/snackbag/mcvera/test/StyleTestApplication.java | 10 +++++----- .../java/net/snackbag/mcvera/test/TestApplication.java | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java index 4a862b13..777e3bf6 100644 --- a/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/StyleTestApplication.java @@ -19,9 +19,9 @@ public class StyleTestApplication extends VeraApp { .loopMode(VLoopMode.FORWARD_REPEAT) .unwindTime(1000) - .keyframe(1000, 2000, frame -> frame.style("background-color", VColor.MC_RED)) - .keyframe(1000, 5000, frame -> frame.style("background-color", VColor.MC_GOLD)) - .keyframe(1000, 1000, frame -> frame.style("background-color", VColor.MC_WHITE)) + .keyframe(1000, 2000, frame -> frame.style("background", VColor.MC_RED)) + .keyframe(1000, 5000, frame -> frame.style("background", VColor.MC_GOLD)) + .keyframe(1000, 1000, frame -> frame.style("background", VColor.MC_WHITE)) .build(); @Override @@ -38,8 +38,8 @@ public void init() { testRect.onRightClick(() -> testRect.animations.unwind(longTestAnimation)); testRect.setStyle("transition", 100); - testRect.setStyle("background-color", VStyleState.HOVERED, VColor.white()); - testRect.setStyle("background-color", VStyleState.CLICKED, VColor.MC_RED); + testRect.setStyle("background", VStyleState.HOVERED, VColor.white()); + testRect.setStyle("background", VStyleState.CLICKED, VColor.MC_RED); // Moving & classes VLabel testLabel = new VLabel("hello there", 40, 10, this) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 7d0b95f9..3d53d524 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -3,6 +3,7 @@ import net.minecraft.util.Identifier; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VFont; +import net.snackbag.vera.core.VImage; import net.snackbag.vera.flag.VHAlignmentFlag; import net.snackbag.vera.core.VColor; import net.snackbag.vera.core.VCursorShape; @@ -88,8 +89,8 @@ public void init() { rightLabel.setStyle("border-size", 1); rightLabel.onRightClick(() -> System.out.println(Vera.openFileSelector("test", Path.of("/Volumes/Media"), null))); - VImage image = new VImage( - "minecraft:textures/block/dirt.png", + VRect image = new VRect( + new VImage("minecraft:textures/block/dirt.png"), 32, 32, this).alsoAdd(); image.move(0, 30); image.onMiddleClick(this::hideCursor); From ac72bd04f2474d1676dba83b193a386c2b25fc66 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:16:14 +0100 Subject: [PATCH 622/661] render fills, of course! --- .../snackbag/mcvera/impl/MCVeraRenderer.java | 94 +++++++++++++++---- 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 6569ec9e..15761b06 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -12,12 +12,9 @@ import net.snackbag.mcvera.MCVeraData; import net.snackbag.mcvera.mixin.DrawContextAccessor; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VColor; -import net.snackbag.vera.core.VFont; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.*; import net.snackbag.vera.flag.VAppFlag; import net.snackbag.vera.flag.VAppPositioningFlag; -import net.snackbag.vera.core.VRenderContext; import net.snackbag.vera.widget.VWidget; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; @@ -88,6 +85,10 @@ public void drawImage(VRenderContext ctx, int x, int y, int width, int height, I ); } + public void drawFill(VRenderContext ctx, int x, int y, int width, int height, VFill fill) { + fill.renderQuad(ctx, x, y, width, height); + } + // // Basic rendering // @@ -256,13 +257,68 @@ public void renderTexQuad( int v2x, int v2y, int v3x, int v3y, int v4x, int v4y + ) { + VColor color = VColor.white(); + + renderTexQuad( + hasTransparentParts, texture, + v1x, v1y, 0.0f, 0.0f, color, + v2x, v2y, 0.0f, 1.0f, color, + v3x, v3y, 1.0f, 1.0f, color, + v4x, v4y, 1.0f, 0.0f, color + ); + } + + /** + * Renders a textured quad to the GUI render layer using the full texture. + * + *

The texture is automatically bound via the provided + * {@link net.minecraft.util.Identifier}.

+ * + *

Blending is automatically enabled and disabled based on + * {@code hasTransparentParts}.

+ * + *

Vertices must be provided in counter-clockwise order in screen space + * (Minecraft GUI coordinates, where Y increases downward):

+ * + *
+     * v1 ── v4
+     * │     │
+     * v2 ── v3
+     * 
+ * + *

Texture coordinates are automatically mapped to the full texture + * (u,v in the range 0.0–1.0).

+ * + *

The vertex buffer is flushed immediately.

+ * + * @param hasTransparentParts whether the texture has transparent parts; handles blending + * @param texture texture identifier to bind + * @param color tint to render with + * @param v1x top-left x + * @param v1y top-left y + * @param v2x bottom-left x + * @param v2y bottom-left y + * @param v3x bottom-right x + * @param v3y bottom-right y + * @param v4x top-right x + * @param v4y top-right y + */ + public void renderTexQuad( + boolean hasTransparentParts, + Identifier texture, + VColor color, + int v1x, int v1y, + int v2x, int v2y, + int v3x, int v3y, + int v4x, int v4y ) { renderTexQuad( hasTransparentParts, texture, - v1x, v1y, 0.0f, 0.0f, - v2x, v2y, 0.0f, 1.0f, - v3x, v3y, 1.0f, 1.0f, - v4x, v4y, 1.0f, 0.0f + v1x, v1y, 0.0f, 0.0f, color, + v2x, v2y, 0.0f, 1.0f, color, + v3x, v3y, 1.0f, 1.0f, color, + v4x, v4y, 1.0f, 0.0f, color ); } @@ -288,26 +344,30 @@ public void renderTexQuad( * @param v1y top-left y * @param u1 texture u at v1 * @param v1t texture v at v1 + * @param v1c tint at v1 * @param v2x bottom-left x * @param v2y bottom-left y * @param u2 texture u at v2 * @param v2t texture v at v2 + * @param v2c tint at v2 * @param v3x bottom-right x * @param v3y bottom-right y * @param u3 texture u at v3 * @param v3t texture v at v3 + * @param v3c tint at v3 * @param v4x top-right x * @param v4y top-right y * @param u4 texture u at v4 * @param v4t texture v at v4 + * @param v4c tint at v4 */ public void renderTexQuad( boolean hasTransparentParts, Identifier texture, - int v1x, int v1y, float u1, float v1t, - int v2x, int v2y, float u2, float v2t, - int v3x, int v3y, float u3, float v3t, - int v4x, int v4y, float u4, float v4t + int v1x, int v1y, float u1, float v1t, VColor v1c, + int v2x, int v2y, float u2, float v2t, VColor v2c, + int v3x, int v3y, float u3, float v3t, VColor v3c, + int v4x, int v4y, float u4, float v4t, VColor v4c ) { RenderSystem.setShaderTexture(0, texture); RenderSystem.setShader(GameRenderer::getPositionTexProgram); @@ -316,11 +376,11 @@ public void renderTexQuad( Matrix4f matrix = drawContext.getMatrices().peek().getPositionMatrix(); BufferBuilder buf = Tessellator.getInstance().getBuffer(); - buf.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); - buf.vertex(matrix, v1x, v1y, 0f).texture(u1, v1t).next(); - buf.vertex(matrix, v2x, v2y, 0f).texture(u2, v2t).next(); - buf.vertex(matrix, v3x, v3y, 0f).texture(u3, v3t).next(); - buf.vertex(matrix, v4x, v4y, 0f).texture(u4, v4t).next(); + buf.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_TEXTURE); + buf.vertex(matrix, v1x, v1y, 0f).color(v1c.toIntArgb()).texture(u1, v1t).next(); + buf.vertex(matrix, v2x, v2y, 0f).color(v2c.toIntArgb()).texture(u2, v2t).next(); + buf.vertex(matrix, v3x, v3y, 0f).color(v3c.toIntArgb()).texture(u3, v3t).next(); + buf.vertex(matrix, v4x, v4y, 0f).color(v4c.toIntArgb()).texture(u4, v4t).next(); BufferRenderer.drawWithGlobalProgram(buf.end()); if (hasTransparentParts) RenderSystem.disableBlend(); From 33388a6bd6be70c57b5d105383d114e22ebf805a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:16:22 +0100 Subject: [PATCH 623/661] rect now supports fill --- .../vera/style/standard/RectStandardStyle.java | 4 ++-- .../java/net/snackbag/vera/widget/VRect.java | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java index b3c683a5..f24d8d64 100644 --- a/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/RectStandardStyle.java @@ -8,11 +8,11 @@ public class RectStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { - sheet.setKey(VRect.class, "background-color", VColor.black()); + sheet.setKey(VRect.class, "background", VColor.black()); } @Override public void reserve(VStyleSheet sheet) { - sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("background", StyleValueType.FILL); } } diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index e13ec4a5..1d288e31 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -1,31 +1,32 @@ package net.snackbag.vera.widget; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VColor; +import net.snackbag.vera.core.VFill; +import net.snackbag.vera.core.VImage; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.core.VRenderContext; public class VRect extends VWidget { - public VRect(VColor color, VeraApp app) { - this(color, 0, 0, 20, 20, app); + public VRect(VFill background, VeraApp app) { + this(background, 0, 0, 20, 20, app); } - public VRect(VColor color, int x, int y, VeraApp app) { - this(color, x, y, 20, 20, app); + public VRect(VFill background, int x, int y, VeraApp app) { + this(background, x, y, 20, 20, app); } - public VRect(VColor color, int x, int y, int width, int height, VeraApp app) { + public VRect(VFill background, int x, int y, int width, int height, VeraApp app) { super(x, y, width, height, app); this.focusOnClick = false; - setStyle("background-color", color); + setStyle("background", background); } @Override public void render(VRenderContext ctx) { VStyleState state = createStyleState(); - Vera.renderer.drawRect(ctx, 0, 0, width, height, getStyle("background-color", state)); + Vera.renderer.drawFill(ctx, 0, 0, width, height, getStyle("background", state)); } } From 8c63f9645431b9402a2767d4679caf09685d650b Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:16:26 +0100 Subject: [PATCH 624/661] retire VImage --- .../java/net/snackbag/vera/widget/VImage.java | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/widget/VImage.java diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java deleted file mode 100644 index 1773829d..00000000 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.snackbag.vera.widget; - -import net.minecraft.util.Identifier; -import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.core.VRenderContext; - -public class VImage extends VWidget { - public VImage(Identifier src, int width, int height, VeraApp app) { - super(0, 0, width, height, app); - - setStyle("src", src); - this.focusOnClick = false; - } - - public VImage(String src, int width, int height, VeraApp app) { - this(new Identifier(src), width, height, app); - } - - @Override - public void render(VRenderContext ctx) { - VStyleState state = createStyleState(); - Identifier src = getStyle("src", state); - - Vera.renderer.drawImage(ctx, 0, 0, width, height, src); - } -} From 81274c0441d2748ecc88a05f757e8860dd0b4fab Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 20:16:26 +0100 Subject: [PATCH 625/661] retire VImage --- .../style/standard/ImageStandardStyle.java | 15 ---------- .../java/net/snackbag/vera/widget/VImage.java | 28 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java delete mode 100644 src/main/java/net/snackbag/vera/widget/VImage.java diff --git a/src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java deleted file mode 100644 index 4c9d2839..00000000 --- a/src/main/java/net/snackbag/vera/style/standard/ImageStandardStyle.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.snackbag.vera.style.standard; - -import net.snackbag.vera.style.StyleValueType; -import net.snackbag.vera.style.VStyleSheet; - -public class ImageStandardStyle implements VStandardStyle { - @Override - public void apply(VStyleSheet sheet) { - } - - @Override - public void reserve(VStyleSheet sheet) { - sheet.reserveType("src", StyleValueType.IDENTIFIER); - } -} diff --git a/src/main/java/net/snackbag/vera/widget/VImage.java b/src/main/java/net/snackbag/vera/widget/VImage.java deleted file mode 100644 index 1773829d..00000000 --- a/src/main/java/net/snackbag/vera/widget/VImage.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.snackbag.vera.widget; - -import net.minecraft.util.Identifier; -import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; -import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.core.VRenderContext; - -public class VImage extends VWidget { - public VImage(Identifier src, int width, int height, VeraApp app) { - super(0, 0, width, height, app); - - setStyle("src", src); - this.focusOnClick = false; - } - - public VImage(String src, int width, int height, VeraApp app) { - this(new Identifier(src), width, height, app); - } - - @Override - public void render(VRenderContext ctx) { - VStyleState state = createStyleState(); - Identifier src = getStyle("src", state); - - Vera.renderer.drawImage(ctx, 0, 0, width, height, src); - } -} From 6ad1ed30fb8b42811fee8363efb31d1a6c603663 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:07:12 +0100 Subject: [PATCH 626/661] use VFill instead of VColor in VLineInput --- .../vera/style/standard/LineInputStandardStyle.java | 8 ++++---- src/main/java/net/snackbag/vera/widget/VLineInput.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java index 49a841f0..71770b66 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LineInputStandardStyle.java @@ -12,8 +12,8 @@ public class LineInputStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { - sheet.setKey(VLineInput.class, "select-color", VColor.of(0, 120, 215, 0.2f)); - sheet.setKey(VLineInput.class, "background-color", VColor.transparent()); + sheet.setKey(VLineInput.class, "select", VColor.of(0, 120, 215, 0.2f)); + sheet.setKey(VLineInput.class, "background", VColor.white()); sheet.setKey(VLineInput.class, "cursor", VCursorShape.TEXT, VStyleState.HOVERED); sheet.setKey(VLineInput.class, "font", VFont.create()); sheet.setKey(VLineInput.class, "placeholder-font", VFont.create().withColor(VColor.black().withOpacity(0.5f))); @@ -22,8 +22,8 @@ public void apply(VStyleSheet sheet) { @Override public void reserve(VStyleSheet sheet) { - sheet.reserveType("select-color", StyleValueType.COLOR); - sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("select", StyleValueType.FILL); + sheet.reserveType("background", StyleValueType.FILL); sheet.reserveType("font", StyleValueType.FONT); sheet.reserveType("placeholder-font", StyleValueType.FONT); sheet.reserveType("padding", StyleValueType.V4INT); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index b50dc0fb..45841f5f 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -43,13 +43,13 @@ public void render(VRenderContext ctx) { VFont font = getStyle("font", state); VFont placeholderFont = getStyle("placeholder-font", state); - VColor bgColor = getStyle("background-color", state); - VColor textSelectionColor = getStyle("select-color", state); + VFill background = getStyle("background-color", state); + VFill textSelectionFill = getStyle("select", state); V4Int padding = getStyle("padding", state); String rText = getTextInViewport(); // background - Vera.renderer.drawRect(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), bgColor); + Vera.renderer.drawFill(ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), background); // text selection int textHeight = Vera.provider.getTextHeight(text, font); @@ -75,7 +75,7 @@ public void render(VRenderContext ctx) { int selTextWidth = Vera.provider.getTextWidth(selInView, font); int selTextHeight = Vera.provider.getTextHeight(selInView, font); - Vera.renderer.drawRect(ctx, startX, textY, selTextWidth, selTextHeight, textSelectionColor); + Vera.renderer.drawFill(ctx, startX, textY, selTextWidth, selTextHeight, textSelectionFill); } } From 75aeadd45f77034f2406b0f72acea0edeb714366 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:12:33 +0100 Subject: [PATCH 627/661] add & implement isVisible --- src/main/java/net/snackbag/vera/core/VColor.java | 10 ++++++++++ src/main/java/net/snackbag/vera/core/VFill.java | 6 ++++++ src/main/java/net/snackbag/vera/core/VImage.java | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 2e2953cc..78655fae 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -198,6 +198,16 @@ public VFill ease(VEasing easing, VFill targetRaw, float delta) { ); } + @Override + public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) { + Vera.renderer.drawRect(ctx, x, y, width, height, this); + } + + @Override + public boolean isVisible() { + return opacity == 0f; + } + public static VColor transparent() { return new VColor(0, 0, 0, 0); } diff --git a/src/main/java/net/snackbag/vera/core/VFill.java b/src/main/java/net/snackbag/vera/core/VFill.java index 6044e8f8..6f893273 100644 --- a/src/main/java/net/snackbag/vera/core/VFill.java +++ b/src/main/java/net/snackbag/vera/core/VFill.java @@ -12,9 +12,15 @@ public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) public VFill ease(VEasing easing, VFill target, float delta) { return target; } + + @Override + public boolean isVisible() { + return true; + } }; } void renderQuad(VRenderContext ctx, int x, int y, int width, int height); VFill ease(VEasing easing, VFill target, float delta); + boolean isVisible(); } diff --git a/src/main/java/net/snackbag/vera/core/VImage.java b/src/main/java/net/snackbag/vera/core/VImage.java index 9b79f818..5e287d75 100644 --- a/src/main/java/net/snackbag/vera/core/VImage.java +++ b/src/main/java/net/snackbag/vera/core/VImage.java @@ -42,4 +42,9 @@ public VFill ease(VEasing easing, VFill targetRaw, float delta) { (VColor) tint.ease(easing, target.tint, delta) ); } + + @Override + public boolean isVisible() { + return tint.isVisible(); + } } From 720acf6f007a4d87d7704d82c0a065e2c1c949d0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:16:21 +0100 Subject: [PATCH 628/661] fix isVisible --- src/main/java/net/snackbag/vera/core/VColor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 78655fae..99ddb88a 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -205,7 +205,7 @@ public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) @Override public boolean isVisible() { - return opacity == 0f; + return opacity != 0f; } public static VColor transparent() { From a5a3bc73f413ff6f2a52d9f099beb323d0736715 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:16:51 +0100 Subject: [PATCH 629/661] label background color is now fill --- .../snackbag/vera/style/standard/LabelStandardStyle.java | 4 ++-- src/main/java/net/snackbag/vera/widget/VLabel.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java index 03d87ca0..4d8b66c1 100644 --- a/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/LabelStandardStyle.java @@ -10,14 +10,14 @@ public class LabelStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { - sheet.setKey(VLabel.class, "background-color", VColor.transparent()); + sheet.setKey(VLabel.class, "background", VColor.transparent()); sheet.setKey(VLabel.class, "font", VFont.create()); sheet.setKey(VLabel.class, "padding", new V4Int(0)); } @Override public void reserve(VStyleSheet sheet) { - sheet.reserveType("background-color", StyleValueType.COLOR); + sheet.reserveType("background", StyleValueType.FILL); sheet.reserveType("font", StyleValueType.FONT); sheet.reserveType("padding", StyleValueType.V4INT); } diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index cbf2c422..5e452d64 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -79,17 +79,17 @@ public void render(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); - VColor backgroundColor = getStyle("background-color", state); + VFill background = getStyle("background", state); V4Int padding = getStyle("padding", state); - if (!backgroundColor.isTransparent()) { - Vera.renderer.drawRect( + if (!background.isVisible()) { + Vera.renderer.drawFill( ctx, 0, 0, getEffectiveWidth(), getEffectiveHeight(), - backgroundColor + background ); } From 2942761aa10b9aefabb65bc44ed7ee89f0ff67ae Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:17:08 +0100 Subject: [PATCH 630/661] retire isTransparent in favour of isVisible --- src/main/java/net/snackbag/vera/core/VColor.java | 11 ++++++----- src/main/java/net/snackbag/vera/core/VeraApp.java | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VColor.java b/src/main/java/net/snackbag/vera/core/VColor.java index 99ddb88a..55bfd4ca 100644 --- a/src/main/java/net/snackbag/vera/core/VColor.java +++ b/src/main/java/net/snackbag/vera/core/VColor.java @@ -89,6 +89,12 @@ public int denormalizedOpacity() { return (int) (opacity * 255); } + /** + * Use {@link #isVisible()} instead. + * + * @return whether the color is transparent + */ + @Deprecated(since = "2.0") public boolean isTransparent() { return opacity == 0; } @@ -220,11 +226,6 @@ public static VColor black() { return new VColor(0, 0, 0, 1); } - @Override - public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) { - Vera.renderer.drawRect(ctx, x, y, width, height, this); - } - public static class ColorModifier { private VColor color; private final Consumer colorUpdater; diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 88bde5d0..eb0f7697 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -209,7 +209,7 @@ public void renderAfterWidgets() {} public void renderHierarchyOverlay() { Vera.renderer.drawRect(x, y, width, height, - backgroundColor.isTransparent() + backgroundColor.isVisible() ? VColor.black().withOpacity(0.2f) : backgroundColor.sub(40).withOpacity(0.2f) ); From b9d5e9000d2dfcd95e67839d5e4dd3c637767170 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:20:29 +0100 Subject: [PATCH 631/661] use background instead of background-color for labels --- .../java/net/snackbag/mcvera/test/TestApplication.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 3d53d524..2c454a7a 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -47,7 +47,7 @@ public void init() { input.onMouseMove((x, y) -> System.out.println("x=" + x + ", y=" + y)); input.move(50); - input.setStyle("background-color", VColor.white()); + input.setStyle("background", VColor.white()); setFocusedWidget(input); VLabel label = new VLabel("Hello world!", this).alsoAdd(); @@ -62,7 +62,7 @@ public void init() { label.setStyle("padding", 5); label.move(10); - label.setStyle("background-color", VColor.black()); + label.setStyle("background", VColor.black()); label.modifyFont().color(VColor.white()); label.adjustSize(); label.onHover(() -> { @@ -75,7 +75,7 @@ public void init() { VLabel centerLabel = new VLabel("CENTER", 220, 10, 100, 16, this).alsoAdd(); centerLabel.setAlignment(VHAlignmentFlag.CENTER); - centerLabel.setStyle("background-color", VColor.black()); + centerLabel.setStyle("background", VColor.black()); centerLabel.modifyFontColor().rgb(255, 255, 255); centerLabel.setStyle("border-color", VColor.MC_BLUE, VColor.MC_GOLD, VColor.MC_RED, VColor.MC_GREEN); centerLabel.setStyle("border-size", 5, 10, 8, 16); @@ -83,7 +83,7 @@ public void init() { VLabel rightLabel = new VLabel("RIGHT", 100, 10, 100, 16, this).alsoAdd(); rightLabel.setAlignment(VHAlignmentFlag.RIGHT); - rightLabel.setStyle("background-color", VColor.black()); + rightLabel.setStyle("background", VColor.black()); rightLabel.modifyFontColor().rgb(255, 255, 255); rightLabel.setStyle("border-color", VColor.white()); rightLabel.setStyle("border-size", 1); From 6b5eb2dc86704fa94524d69cc2cb983cad2eedd3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:21:33 +0100 Subject: [PATCH 632/661] fix NPE due to background-color style key --- src/main/java/net/snackbag/vera/widget/VLineInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 45841f5f..0268d27e 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -43,7 +43,7 @@ public void render(VRenderContext ctx) { VFont font = getStyle("font", state); VFont placeholderFont = getStyle("placeholder-font", state); - VFill background = getStyle("background-color", state); + VFill background = getStyle("background", state); VFill textSelectionFill = getStyle("select", state); V4Int padding = getStyle("padding", state); String rText = getTextInViewport(); From 52d0b3f578c663b90537a974c967b91ac5a293b3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:23:02 +0100 Subject: [PATCH 633/661] im dumb --- src/main/java/net/snackbag/vera/widget/VLabel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 5e452d64..06bb3555 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -82,7 +82,7 @@ public void render(VRenderContext ctx) { VFill background = getStyle("background", state); V4Int padding = getStyle("padding", state); - if (!background.isVisible()) { + if (background.isVisible()) { Vera.renderer.drawFill( ctx, 0, From f712376a0beb9d1cb5638f0d0377ed204196d7d1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:28:16 +0100 Subject: [PATCH 634/661] use fill instead of image --- .../style/standard/CheckBoxStandardStyle.java | 9 ++++++-- .../net/snackbag/vera/widget/VCheckBox.java | 21 ++++++------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java index 7c957c39..26971e74 100644 --- a/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java +++ b/src/main/java/net/snackbag/vera/style/standard/CheckBoxStandardStyle.java @@ -1,6 +1,9 @@ package net.snackbag.vera.style.standard; +import net.minecraft.util.Identifier; +import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.core.VCursorShape; +import net.snackbag.vera.core.VImage; import net.snackbag.vera.style.VStyleState; import net.snackbag.vera.style.StyleValueType; import net.snackbag.vera.style.VStyleSheet; @@ -10,11 +13,13 @@ public class CheckBoxStandardStyle implements VStandardStyle { @Override public void apply(VStyleSheet sheet) { sheet.setKey(VCheckBox.class, "cursor", VCursorShape.POINTING_HAND, VStyleState.HOVERED); + sheet.setKey(VCheckBox.class, "fill", new VImage(new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/default.png"))); + sheet.setKey(VCheckBox.class, "fill-checked", new VImage(new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/checked.png"))); } @Override public void reserve(VStyleSheet sheet) { - sheet.reserveType("src", StyleValueType.IDENTIFIER); - sheet.reserveType("src-checked", StyleValueType.IDENTIFIER); + sheet.reserveType("fill", StyleValueType.FILL); + sheet.reserveType("fill-checked", StyleValueType.FILL); } } diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index f923152d..1443c79f 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -3,6 +3,8 @@ import net.minecraft.util.Identifier; import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VFill; +import net.snackbag.vera.core.VImage; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCheckedStateChange; @@ -13,32 +15,21 @@ public class VCheckBox extends VWidget { private boolean checked; public VCheckBox(VeraApp app) { - this( - app, - new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/default.png"), - new Identifier(MinecraftVera.MOD_ID, "widgets/checkmark/checked.png") - ); + this(app, 15, 15); } - public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTexture) { - this(app, defaultTexture, checkedTexture, 15, 15); - } - - public VCheckBox(VeraApp app, Identifier defaultTexture, Identifier checkedTexture, int width, int height) { + public VCheckBox(VeraApp app, int width, int height) { super(0, 0, width, height, app); this.checked = false; - - setStyle("src", defaultTexture); - setStyle("src-checked", checkedTexture); } @Override public void render(VRenderContext ctx) { VStyleState state = createStyleState(); - Identifier texture = checked ? getStyle("src-checked", state) : getStyle("src", state); + VFill fill = checked ? getStyle("fill-checked", state) : getStyle("fill", state); - Vera.renderer.drawImage(ctx, 0, 0, width, height, texture); + Vera.renderer.drawFill(ctx, 0, 0, width, height, fill); } @Override From 376ed165a879951ad0dfd42b33a43a4669287a02 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:28:51 +0100 Subject: [PATCH 635/661] fix hover state for VRect --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 2c454a7a..6f3ca7e4 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -95,7 +95,7 @@ public void init() { image.move(0, 30); image.onMiddleClick(this::hideCursor); image.onMiddleClickRelease(this::showCursor); - image.setStyle("src", VStyleState.HOVERED, "minecraft:textures/block/diamond_block.png"); + image.setStyle("background", VStyleState.HOVERED, "minecraft:textures/block/diamond_block.png"); VDropdown dropdown = new VDropdown(this).alsoAdd(); dropdown.addItem("coolio"); From f78dcc9dabe72fcb94b2d588ba2b12ab2d205f07 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:34:03 +0100 Subject: [PATCH 636/661] fix only first pixel of texture being rendered --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 15761b06..c733c707 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -370,7 +370,7 @@ public void renderTexQuad( int v4x, int v4y, float u4, float v4t, VColor v4c ) { RenderSystem.setShaderTexture(0, texture); - RenderSystem.setShader(GameRenderer::getPositionTexProgram); + RenderSystem.setShader(GameRenderer::getPositionColorTexProgram); if (hasTransparentParts) RenderSystem.enableBlend(); Matrix4f matrix = drawContext.getMatrices().peek().getPositionMatrix(); From e9520da836940dff32267c008bf7a64a10a8a13f Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:34:14 +0100 Subject: [PATCH 637/661] ease rendering --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index c733c707..e6352fe2 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -261,11 +261,8 @@ public void renderTexQuad( VColor color = VColor.white(); renderTexQuad( - hasTransparentParts, texture, - v1x, v1y, 0.0f, 0.0f, color, - v2x, v2y, 0.0f, 1.0f, color, - v3x, v3y, 1.0f, 1.0f, color, - v4x, v4y, 1.0f, 0.0f, color + hasTransparentParts, texture, color, + v1x, v1y, v2x, v2y, v3x, v3y, v4x, v4y ); } From a502fb53e61e05b225625a193435e60e7c18b0ad Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:56:22 +0100 Subject: [PATCH 638/661] fix sizing --- src/main/java/net/snackbag/mcvera/test/TestApplication.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/test/TestApplication.java b/src/main/java/net/snackbag/mcvera/test/TestApplication.java index 6f3ca7e4..50049fef 100644 --- a/src/main/java/net/snackbag/mcvera/test/TestApplication.java +++ b/src/main/java/net/snackbag/mcvera/test/TestApplication.java @@ -91,8 +91,7 @@ public void init() { VRect image = new VRect( new VImage("minecraft:textures/block/dirt.png"), - 32, 32, this).alsoAdd(); - image.move(0, 30); + 0, 30, 32, 32, this).alsoAdd(); image.onMiddleClick(this::hideCursor); image.onMiddleClickRelease(this::showCursor); image.setStyle("background", VStyleState.HOVERED, "minecraft:textures/block/diamond_block.png"); From 129b40a95adb9ef4177e3395988910ac608ea81c Mon Sep 17 00:00:00 2001 From: JXSnack Date: Wed, 25 Feb 2026 21:58:58 +0100 Subject: [PATCH 639/661] render via quad renderer so we can render the tint --- src/main/java/net/snackbag/vera/core/VImage.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VImage.java b/src/main/java/net/snackbag/vera/core/VImage.java index 5e287d75..344b08cd 100644 --- a/src/main/java/net/snackbag/vera/core/VImage.java +++ b/src/main/java/net/snackbag/vera/core/VImage.java @@ -28,7 +28,13 @@ public VImage(Identifier src) { @Override public void renderQuad(VRenderContext ctx, int x, int y, int width, int height) { - Vera.renderer.drawImage(ctx, x, y, width, height, src); + Vera.renderer.renderTexQuad( + ctx.hasTransparency, src, tint, + x, y, + x, y + height, + x + width, y + height, + x + width, y + ); } @Override From f001cddb5a55272f549ab8fcb998bf43e834dfdd Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 16:59:02 +0100 Subject: [PATCH 640/661] move ensureClearContext method up --- .../net/snackbag/mcvera/impl/MCVeraRenderer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index e6352fe2..a176fc36 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -387,6 +387,12 @@ public void renderTexQuad( // Apps // + private void ensureClearContext(VRenderContext ctx) { + if (ctx.peekClip() != null) { + throw new RuntimeException("Unclosed clip stack"); + } + } + public void renderApp(VeraApp app) { boolean blendEnabled = GL11.glIsEnabled(GL11.GL_BLEND); @@ -445,10 +451,4 @@ public void renderApps(VAppPositioningFlag flag) { Vera.renderer.renderApp(app); } } - - private void ensureClearContext(VRenderContext ctx) { - if (ctx.peekClip() != null) { - throw new RuntimeException("Unclosed clip stack"); - } - } } From 5d999227aa5731ae0f4e040a79897f0e7d2b0bfe Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 17:00:22 +0100 Subject: [PATCH 641/661] rename render method to renderContent --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- src/main/java/net/snackbag/vera/widget/VCheckBox.java | 2 +- src/main/java/net/snackbag/vera/widget/VDropdown.java | 2 +- src/main/java/net/snackbag/vera/widget/VLabel.java | 2 +- src/main/java/net/snackbag/vera/widget/VLineInput.java | 2 +- src/main/java/net/snackbag/vera/widget/VRect.java | 2 +- src/main/java/net/snackbag/vera/widget/VTabWidget.java | 2 +- src/main/java/net/snackbag/vera/widget/VWidget.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index a176fc36..59aaed34 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -415,7 +415,7 @@ public void renderApp(VeraApp app) { VRenderContext ctx = widget.createRenderContext(); pushContext(ctx); - widget.render(ctx); + widget.renderContent(ctx); widget.renderBorder(ctx); widget.renderOverlay(ctx); diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 1443c79f..78d9621b 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -25,7 +25,7 @@ public VCheckBox(VeraApp app, int width, int height) { } @Override - public void render(VRenderContext ctx) { + public void renderContent(VRenderContext ctx) { VStyleState state = createStyleState(); VFill fill = checked ? getStyle("fill-checked", state) : getStyle("fill", state); diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 167dc057..2285677b 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -37,7 +37,7 @@ public VDropdown(VeraApp app) { } @Override - public void render(VRenderContext ctx) { + public void renderContent(VRenderContext ctx) { VStyleState state = createStyleState(); VColor backgroundColor = getStyle("background-color", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index 06bb3555..a67d537a 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -75,7 +75,7 @@ public VColor.ColorModifier modifyColor(String key) { } @Override - public void render(VRenderContext ctx) { + public void renderContent(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 0268d27e..7ea775e4 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -38,7 +38,7 @@ public VLineInput(VeraApp app) { } @Override - public void render(VRenderContext ctx) { + public void renderContent(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 1d288e31..503930e1 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -24,7 +24,7 @@ public VRect(VFill background, int x, int y, int width, int height, VeraApp app) } @Override - public void render(VRenderContext ctx) { + public void renderContent(VRenderContext ctx) { VStyleState state = createStyleState(); Vera.renderer.drawFill(ctx, 0, 0, width, height, getStyle("background", state)); diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index f1b4f036..da15a87b 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -23,7 +23,7 @@ public VTabWidget(VeraApp app) { } @Override - public void render(VRenderContext ctx) { + public void renderContent(VRenderContext ctx) { VStyleState state = createStyleState(); VFont font = getStyle("font", state); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 2c90854c..79f1d59a 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -42,7 +42,7 @@ public VWidget(int x, int y, int width, int height, VeraApp app) { this.hasTransparency = false; } - public abstract void render(VRenderContext ctx); + public abstract void renderContent(VRenderContext ctx); public int getHitboxX() { return getX(); From c619108248d4b37c9a55b105c121bbfd71fae0a0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 17:31:11 +0100 Subject: [PATCH 642/661] make ensureClearContext public --- src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index 59aaed34..cd882355 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -387,7 +387,7 @@ public void renderTexQuad( // Apps // - private void ensureClearContext(VRenderContext ctx) { + public void ensureClearContext(VRenderContext ctx) { if (ctx.peekClip() != null) { throw new RuntimeException("Unclosed clip stack"); } From 1efa6022e72ef822968590f4d8b5d6673dbeca86 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 17:31:30 +0100 Subject: [PATCH 643/661] move widget rendering logic into the widget itself --- .../snackbag/mcvera/impl/MCVeraRenderer.java | 17 +---------------- .../net/snackbag/vera/widget/VWidget.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java index cd882355..04e32839 100644 --- a/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java +++ b/src/main/java/net/snackbag/mcvera/impl/MCVeraRenderer.java @@ -408,22 +408,7 @@ public void renderApp(VeraApp app) { if (widget != hoveredWidget && widget.isHovered()) widget.setHovered(false); else if (widget == hoveredWidget && !widget.isHovered()) widget.setHovered(true); - widget.beforeRender(); - widget.animations.updateLifetimes(); - - if (widget.visibilityConditionsPassed()) { - VRenderContext ctx = widget.createRenderContext(); - pushContext(ctx); - - widget.renderContent(ctx); - widget.renderBorder(ctx); - widget.renderOverlay(ctx); - - ensureClearContext(ctx); - popContext(); - } - - widget.afterRender(); + widget.renderSelf(); } app.renderAfterWidgets(); diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 79f1d59a..98e4aa2c 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -168,6 +168,25 @@ public VRenderContext createRenderContext() { ); } + public void renderSelf() { + beforeRender(); + animations.updateLifetimes(); + + if (visibilityConditionsPassed()) { + VRenderContext ctx = createRenderContext(); + Vera.renderer.pushContext(ctx); + + renderContent(ctx); + renderBorder(ctx); + renderOverlay(ctx); + + Vera.renderer.ensureClearContext(ctx); + Vera.renderer.popContext(); + } + + afterRender(); + } + public void renderBorder(VRenderContext ctx) { // TODO: [Render Rework] Better border rendering From 8f9785a0a68c55d534de3a9998795967648976b5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 17:31:50 +0100 Subject: [PATCH 644/661] add VWidgetContainer interface --- .../snackbag/vera/core/VWidgetContainer.java | 18 ++++++++++++++++++ .../java/net/snackbag/vera/core/VeraApp.java | 19 ++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/core/VWidgetContainer.java diff --git a/src/main/java/net/snackbag/vera/core/VWidgetContainer.java b/src/main/java/net/snackbag/vera/core/VWidgetContainer.java new file mode 100644 index 00000000..8e03d371 --- /dev/null +++ b/src/main/java/net/snackbag/vera/core/VWidgetContainer.java @@ -0,0 +1,18 @@ +package net.snackbag.vera.core; + +import net.snackbag.vera.widget.VWidget; + +import java.util.Collections; +import java.util.List; + +public interface VWidgetContainer { + List> getWidgets(); + void addWidget(VWidget widget); + void removeWidget(VWidget widget); + + default List> getWidgetsReversed() { + List> widgets = getWidgets(); + Collections.reverse(widgets); + return widgets; + } +} diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index eb0f7697..ba9c8bb7 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -16,7 +16,7 @@ import java.util.*; -public abstract class VeraApp { +public abstract class VeraApp implements VWidgetContainer { public final VStyleSheet styleSheet = new VStyleSheet(); private final List> widgets; @@ -165,22 +165,23 @@ public void moveToHierarchyTop() { public abstract void init(); + @Override public List> getWidgets() { return new ArrayList<>(widgets); } - public List> getWidgetsReversed() { - List> widgets = getWidgets(); - Collections.reverse(widgets); - - return widgets; - } - + @Override public void addWidget(VWidget widget) { - if (widgets.contains(widget)) return; + if (widgets.contains(widget)) { + MinecraftVera.LOGGER.error("Can't add widget %s to app %s, because it is already added" + .formatted(widget.toString(), getClass().getSimpleName())); + return; + } + this.widgets.add(widget); } + @Override public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; From e6ca47fc656a55584e84743ebc01b3f29e85c912 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 17:32:03 +0100 Subject: [PATCH 645/661] add getRawX and getRawY methods --- src/main/java/net/snackbag/vera/VElement.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 389de601..809e686d 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -99,6 +99,14 @@ public int getY() { return layout != null ? layout.posOf(this).y : _y; } + public int getRawX() { + return _x; + } + + public int getRawY() { + return _y; + } + @Deprecated(forRemoval = true) public int getEffectiveX() { return getX(); From b88d052c83fc544dd0c61d73613bd15c886a47d0 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 17:42:11 +0100 Subject: [PATCH 646/661] undo VWidgetContainer --- .../snackbag/vera/core/VWidgetContainer.java | 18 ------------------ .../java/net/snackbag/vera/core/VeraApp.java | 11 +++++++---- 2 files changed, 7 insertions(+), 22 deletions(-) delete mode 100644 src/main/java/net/snackbag/vera/core/VWidgetContainer.java diff --git a/src/main/java/net/snackbag/vera/core/VWidgetContainer.java b/src/main/java/net/snackbag/vera/core/VWidgetContainer.java deleted file mode 100644 index 8e03d371..00000000 --- a/src/main/java/net/snackbag/vera/core/VWidgetContainer.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.snackbag.vera.core; - -import net.snackbag.vera.widget.VWidget; - -import java.util.Collections; -import java.util.List; - -public interface VWidgetContainer { - List> getWidgets(); - void addWidget(VWidget widget); - void removeWidget(VWidget widget); - - default List> getWidgetsReversed() { - List> widgets = getWidgets(); - Collections.reverse(widgets); - return widgets; - } -} diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index ba9c8bb7..631c3936 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -16,7 +16,7 @@ import java.util.*; -public abstract class VeraApp implements VWidgetContainer { +public abstract class VeraApp { public final VStyleSheet styleSheet = new VStyleSheet(); private final List> widgets; @@ -165,12 +165,16 @@ public void moveToHierarchyTop() { public abstract void init(); - @Override public List> getWidgets() { return new ArrayList<>(widgets); } - @Override + public List> getWidgetsReversed() { + List> widgets = getWidgets(); + Collections.reverse(widgets); + return widgets; + } + public void addWidget(VWidget widget) { if (widgets.contains(widget)) { MinecraftVera.LOGGER.error("Can't add widget %s to app %s, because it is already added" @@ -181,7 +185,6 @@ public void addWidget(VWidget widget) { this.widgets.add(widget); } - @Override public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; From a58eea83f0416672989c381ec6146ee11909f127 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 18:08:23 +0100 Subject: [PATCH 647/661] add VAppAccess --- src/main/java/net/snackbag/vera/VElement.java | 15 ++++++++----- .../net/snackbag/vera/core/VAppAccess.java | 9 ++++++++ .../java/net/snackbag/vera/core/VeraApp.java | 8 ++++++- .../net/snackbag/vera/layout/VHLayout.java | 2 +- .../net/snackbag/vera/layout/VVLayout.java | 2 +- .../vera/style/animation/AnimationEngine.java | 4 ++-- .../net/snackbag/vera/widget/VCheckBox.java | 11 +++------- .../net/snackbag/vera/widget/VDropdown.java | 7 +----- .../java/net/snackbag/vera/widget/VLabel.java | 13 ++++------- .../net/snackbag/vera/widget/VLineInput.java | 7 +----- .../java/net/snackbag/vera/widget/VRect.java | 11 ++++------ .../net/snackbag/vera/widget/VTabWidget.java | 18 +++++---------- .../net/snackbag/vera/widget/VWidget.java | 22 +++++++++++-------- 13 files changed, 61 insertions(+), 68 deletions(-) create mode 100644 src/main/java/net/snackbag/vera/core/VAppAccess.java diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 809e686d..7e96b58e 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -1,5 +1,6 @@ package net.snackbag.vera; +import net.snackbag.vera.core.VAppAccess; import net.snackbag.vera.core.VeraApp; import net.snackbag.vera.event.EventHandler; import net.snackbag.vera.event.VEvents; @@ -21,13 +22,13 @@ public abstract class VElement { public boolean visible = true; public final EventHandler events; - public final VeraApp app; + public final VAppAccess appAccess; private final List> visibilityConditions = new ArrayList<>(); protected @Nullable VLayout layout; - public VElement(VeraApp app, int x, int y, int width, int height) { - this.app = app; + public VElement(VAppAccess app, int x, int y, int width, int height) { + this.appAccess = app; this.events = new EventHandler(this); this.events.preprocessor = this::handleBuiltinEvent; @@ -44,6 +45,10 @@ public VElement(VeraApp app, int x, int y, int width, int height) { onLayoutRemove(() -> this.layout = null); } + public VeraApp getApp() { + return appAccess.get(); + } + public void handleBuiltinEvent(String name, Object... args) {} public void afterBuiltinEvent(String name, Object... args) {} @@ -118,11 +123,11 @@ public int getEffectiveY() { } public int getRelativeMouseX() { - return Vera.getMouseX() - getX() - app.getX(); + return Vera.getMouseX() - getX() - getApp().getX(); } public int getRelativeMouseY() { - return Vera.getMouseY() - getY() - app.getY(); + return Vera.getMouseY() - getY() - getApp().getY(); } public void move(int both) { diff --git a/src/main/java/net/snackbag/vera/core/VAppAccess.java b/src/main/java/net/snackbag/vera/core/VAppAccess.java new file mode 100644 index 00000000..b438ee9f --- /dev/null +++ b/src/main/java/net/snackbag/vera/core/VAppAccess.java @@ -0,0 +1,9 @@ +package net.snackbag.vera.core; + +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface VAppAccess { + @NotNull + VeraApp get(); +} diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 631c3936..29e436d8 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -11,12 +11,13 @@ import net.snackbag.vera.style.VStyleSheet; import net.snackbag.vera.util.VGeometry; import net.snackbag.vera.widget.VWidget; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import java.util.*; -public abstract class VeraApp { +public abstract class VeraApp implements VAppAccess { public final VStyleSheet styleSheet = new VStyleSheet(); private final List> widgets; @@ -57,6 +58,11 @@ public VeraApp(boolean mouseRequired) { setPositioning(VAppPositioningFlag.SCREEN); } + @Override + public @NotNull VeraApp get() { + return this; + } + public void setCursorVisible(boolean cursorVisible) { this.cursorVisible = cursorVisible; diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index 1aa93627..4db47280 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -14,7 +14,7 @@ public VHLayout(VeraApp app, int x, int y) { } public VHLayout(VLayout parent, int width, int height) { - this(parent.app, 0, 0, width, height); + this(parent.getApp(), 0, 0, width, height); this.alsoAddTo(parent); } diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index d9a6f779..957306e5 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -14,7 +14,7 @@ public VVLayout(VeraApp app, int x, int y) { } public VVLayout(VLayout parent, int width, int height) { - this(parent.app, 0, 0, width, height); + this(parent.getApp(), 0, 0, width, height); this.alsoAddTo(parent); } diff --git a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java index 99317157..c9a4cb32 100644 --- a/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java +++ b/src/main/java/net/snackbag/vera/style/animation/AnimationEngine.java @@ -23,7 +23,7 @@ public void start(VAnimation animation) { return; } - CompiledAnimation compiled = animation.compile(widget.app, widget); + CompiledAnimation compiled = animation.compile(widget.getApp(), widget); active.put(compiled.name, new PlaybackContext(compiled, System.currentTimeMillis())); widget.events.fire(VEvents.Animation.BEGIN, animation); } @@ -109,7 +109,7 @@ public T animateStyle(String key, T value) { float delta = animation.getKeyframeDelta(time, fromKfWhen, from, to); - StyleValueType reservation = widget.app.styleSheet.getReservation(key); + StyleValueType reservation = widget.getApp().styleSheet.getReservation(key); T kfEase = (T) reservation.animationTransition.apply( // ease keyframe transition from.styles.get(key), to.styles.get(key), to.easing, delta); diff --git a/src/main/java/net/snackbag/vera/widget/VCheckBox.java b/src/main/java/net/snackbag/vera/widget/VCheckBox.java index 78d9621b..5f3baa40 100644 --- a/src/main/java/net/snackbag/vera/widget/VCheckBox.java +++ b/src/main/java/net/snackbag/vera/widget/VCheckBox.java @@ -1,24 +1,19 @@ package net.snackbag.vera.widget; -import net.minecraft.util.Identifier; -import net.snackbag.mcvera.MinecraftVera; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VFill; -import net.snackbag.vera.core.VImage; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.*; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.event.VCheckedStateChange; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.core.VRenderContext; public class VCheckBox extends VWidget { private boolean checked; - public VCheckBox(VeraApp app) { + public VCheckBox(VAppAccess app) { this(app, 15, 15); } - public VCheckBox(VeraApp app, int width, int height) { + public VCheckBox(VAppAccess app, int width, int height) { super(0, 0, width, height, app); this.checked = false; diff --git a/src/main/java/net/snackbag/vera/widget/VDropdown.java b/src/main/java/net/snackbag/vera/widget/VDropdown.java index 2285677b..c6270d94 100644 --- a/src/main/java/net/snackbag/vera/widget/VDropdown.java +++ b/src/main/java/net/snackbag/vera/widget/VDropdown.java @@ -28,7 +28,7 @@ public class VDropdown extends VWidget implements VHasFont { private int itemSpacing = 0; private @Nullable Integer hoveredItem = null; - public VDropdown(VeraApp app) { + public VDropdown(VAppAccess app) { super(0, 0, 100, 16, app); items = new ArrayList<>(); @@ -281,11 +281,6 @@ public List getItems() { return items; } - @Override - public VeraApp getApp() { - return app; - } - public static class Item { private String name; private final @Nullable Runnable leftClick; diff --git a/src/main/java/net/snackbag/vera/widget/VLabel.java b/src/main/java/net/snackbag/vera/widget/VLabel.java index a67d537a..00ca091e 100644 --- a/src/main/java/net/snackbag/vera/widget/VLabel.java +++ b/src/main/java/net/snackbag/vera/widget/VLabel.java @@ -12,7 +12,7 @@ public class VLabel extends VWidget implements VHasFont { private String text; private VHAlignmentFlag alignment; - public VLabel(String text, int x, int y, int width, int height, VeraApp app) { + public VLabel(String text, int x, int y, int width, int height, VAppAccess app) { super(x, y, width, height, app); this.text = text; @@ -20,12 +20,12 @@ public VLabel(String text, int x, int y, int width, int height, VeraApp app) { alignment = VHAlignmentFlag.LEFT; } - public VLabel(String text, int x, int y, VeraApp app) { + public VLabel(String text, int x, int y, VAppAccess app) { this(text, x, y, 100, 16, app); adjustSize(); } - public VLabel(String text, VeraApp app) { + public VLabel(String text, VAppAccess app) { this(text, 0, 0, 100, 16, app); adjustSize(); } @@ -65,13 +65,8 @@ public void adjustSize() { this.height = Vera.provider.getTextHeight(text, font); } - @Override - public VeraApp getApp() { - return app; - } - public VColor.ColorModifier modifyColor(String key) { - return app.styleSheet.modifyKeyAsColor(this, key); + return getApp().styleSheet.modifyKeyAsColor(this, key); } @Override diff --git a/src/main/java/net/snackbag/vera/widget/VLineInput.java b/src/main/java/net/snackbag/vera/widget/VLineInput.java index 7ea775e4..eae72f2c 100644 --- a/src/main/java/net/snackbag/vera/widget/VLineInput.java +++ b/src/main/java/net/snackbag/vera/widget/VLineInput.java @@ -26,7 +26,7 @@ public class VLineInput extends VWidget implements VHasFont, VHasPla private long timeSinceLastInput; private int textViewport = 0; - public VLineInput(VeraApp app) { + public VLineInput(VAppAccess app) { super(0, 0, 100, 20, app); this.text = ""; @@ -105,11 +105,6 @@ public void handleBuiltinEvent(String event, Object... args) { } } - @Override - public VeraApp getApp() { - return app; - } - public String getText() { return text; } diff --git a/src/main/java/net/snackbag/vera/widget/VRect.java b/src/main/java/net/snackbag/vera/widget/VRect.java index 503930e1..2ef4cbbc 100644 --- a/src/main/java/net/snackbag/vera/widget/VRect.java +++ b/src/main/java/net/snackbag/vera/widget/VRect.java @@ -1,22 +1,19 @@ package net.snackbag.vera.widget; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VFill; -import net.snackbag.vera.core.VImage; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.*; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.core.VRenderContext; public class VRect extends VWidget { - public VRect(VFill background, VeraApp app) { + public VRect(VFill background, VAppAccess app) { this(background, 0, 0, 20, 20, app); } - public VRect(VFill background, int x, int y, VeraApp app) { + public VRect(VFill background, int x, int y, VAppAccess app) { this(background, x, y, 20, 20, app); } - public VRect(VFill background, int x, int y, int width, int height, VeraApp app) { + public VRect(VFill background, int x, int y, int width, int height, VAppAccess app) { super(x, y, width, height, app); this.focusOnClick = false; diff --git a/src/main/java/net/snackbag/vera/widget/VTabWidget.java b/src/main/java/net/snackbag/vera/widget/VTabWidget.java index da15a87b..456cc0f4 100644 --- a/src/main/java/net/snackbag/vera/widget/VTabWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VTabWidget.java @@ -1,13 +1,10 @@ package net.snackbag.vera.widget; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VColor; -import net.snackbag.vera.core.VFont; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.*; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.modifier.VHasFont; import net.snackbag.vera.style.VStyleState; -import net.snackbag.vera.core.VRenderContext; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -18,7 +15,7 @@ public class VTabWidget extends VWidget implements VHasFont { private @Nullable Integer activeTab = null; private @Nullable Integer hoveredTab = null; - public VTabWidget(VeraApp app) { + public VTabWidget(VAppAccess app) { super(0, 0, 100, 16, app); } @@ -184,7 +181,7 @@ public void addWidget(String tab, VWidget... widgets) { public void addWidget(String tab, List> widgets) { if (tab == null || !tabs.containsKey(tab)) { - throw new IllegalArgumentException("Failed to add " + widgets.size() + " widget(s) to tab '" + tab + "', because it doesn't exist. (App: " + app.getClass().getSimpleName() + ")"); + throw new IllegalArgumentException("Failed to add " + widgets.size() + " widget(s) to tab '" + tab + "', because it doesn't exist. (App: " + getApp().getClass().getSimpleName() + ")"); } Integer tabIndex = getTabIndex(tab); @@ -233,15 +230,10 @@ public void setActiveTab(@Nullable Integer activeTab) { } public VColor.ColorModifier modifyBackgroundColorSelected() { - return app.styleSheet.modifyKeyAsColor(this, "background-color-selected"); + return getApp().styleSheet.modifyKeyAsColor(this, "background-color-selected"); } public VColor.ColorModifier modifyBackgroundColor() { - return app.styleSheet.modifyKeyAsColor(this, "background-color"); - } - - @Override - public VeraApp getApp() { - return app; + return getApp().styleSheet.modifyKeyAsColor(this, "background-color"); } } diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 98e4aa2c..a8fa2f69 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -36,7 +36,7 @@ public abstract class VWidget> extends VElement { public final LinkedHashSet classes = new LinkedHashSet<>(); - public VWidget(int x, int y, int width, int height, VeraApp app) { + public VWidget(int x, int y, int width, int height, VAppAccess app) { super(app, x, y, width, height); this.hasTransparency = false; @@ -62,20 +62,20 @@ public int getHitboxHeight() { @SuppressWarnings("unchecked") public void setStyle(String key, V... value) { - app.styleSheet.setKey(this, key, value); + getApp().styleSheet.setKey(this, key, value); } @SuppressWarnings("unchecked") public void setStyle(String key, VStyleState state, V... value) { - app.styleSheet.setKey(this, key, value, state); + getApp().styleSheet.setKey(this, key, value, state); } public V getStyle(String key) { - return animations.animateStyle(key, app.styleSheet.getKey(this, key)); + return animations.animateStyle(key, getApp().styleSheet.getKey(this, key)); } public V getStyle(String key, VStyleState state) { - return animations.animateStyle(key, app.styleSheet.getKey(this, key, state)); + return animations.animateStyle(key, getApp().styleSheet.getKey(this, key, state)); } public V getStyleOrDefault(String key, V dflt) { @@ -160,6 +160,7 @@ else if (DragHandler.isDragging() && DragHandler.target == this) { public VRenderContext createRenderContext() { VStyleState state = createStyleState(); + VeraApp app = getApp(); return new VRenderContext( app.getX() + getX(), app.getY() + getY(), getEffectiveWidth(), getEffectiveHeight(), @@ -227,6 +228,7 @@ public void renderOverlay(VRenderContext ctx) { } public void beforeRender() { + VeraApp app = getApp(); VStyleState state = createStyleState(); if (state != prevStyleState) { @@ -295,7 +297,7 @@ public void setHasTransparency(boolean hasTransparency) { public void update() { VStyleState state = createStyleState(); - app.setCursorShape(getStyle("cursor", state)); + getApp().setCursorShape(getStyle("cursor", state)); } public boolean isHovered() { @@ -455,10 +457,12 @@ private void clearMiddleClickDown() { } public boolean isFocused() { - return app.isFocusedWidget(this); + return getApp().isFocusedWidget(this); } public void setFocused(boolean focused) { + VeraApp app = getApp(); + if (focused) app.setFocusedWidget(this); else app.setFocusedWidget(null); } @@ -468,7 +472,7 @@ public void keyPressed(int keyCode, int scanCode, int modifiers) {} public void charTyped(char chr, int modifiers) {} public void remove() { - app.removeWidget(this); + getApp().removeWidget(this); } public T alsoAddClass(String clazz) { @@ -477,7 +481,7 @@ public T alsoAddClass(String clazz) { } public T alsoAdd() { - app.addWidget(this); + getApp().addWidget(this); return (T) this; } From e6754a80c53a02c9db762190904727151284ee1d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:10:23 +0100 Subject: [PATCH 648/661] add add/get/remove methods to VAppAccess --- .../java/net/snackbag/vera/core/VAppAccess.java | 15 ++++++++++++++- src/main/java/net/snackbag/vera/core/VeraApp.java | 9 +++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/snackbag/vera/core/VAppAccess.java b/src/main/java/net/snackbag/vera/core/VAppAccess.java index b438ee9f..34a3d43a 100644 --- a/src/main/java/net/snackbag/vera/core/VAppAccess.java +++ b/src/main/java/net/snackbag/vera/core/VAppAccess.java @@ -1,9 +1,22 @@ package net.snackbag.vera.core; +import net.snackbag.vera.widget.VWidget; import org.jetbrains.annotations.NotNull; -@FunctionalInterface +import java.util.Collections; +import java.util.List; + public interface VAppAccess { @NotNull VeraApp get(); + + List> getWidgets(); + void removeWidget(VWidget widget); + void addWidget(VWidget widget); + + default List> getWidgetsReversed() { + List> widgets = getWidgets(); + Collections.reverse(widgets); + return widgets; + } } diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 29e436d8..1d21bba4 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -171,16 +171,12 @@ public void moveToHierarchyTop() { public abstract void init(); + @Override public List> getWidgets() { return new ArrayList<>(widgets); } - public List> getWidgetsReversed() { - List> widgets = getWidgets(); - Collections.reverse(widgets); - return widgets; - } - + @Override public void addWidget(VWidget widget) { if (widgets.contains(widget)) { MinecraftVera.LOGGER.error("Can't add widget %s to app %s, because it is already added" @@ -191,6 +187,7 @@ public void addWidget(VWidget widget) { this.widgets.add(widget); } + @Override public void removeWidget(VWidget widget) { if (!widgets.contains(widget)) return; From 39c20306011df3a066ee128792f28552ad8be560 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:10:41 +0100 Subject: [PATCH 649/661] add error message --- src/main/java/net/snackbag/vera/core/VeraApp.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snackbag/vera/core/VeraApp.java b/src/main/java/net/snackbag/vera/core/VeraApp.java index 1d21bba4..b649e165 100644 --- a/src/main/java/net/snackbag/vera/core/VeraApp.java +++ b/src/main/java/net/snackbag/vera/core/VeraApp.java @@ -189,7 +189,11 @@ public void addWidget(VWidget widget) { @Override public void removeWidget(VWidget widget) { - if (!widgets.contains(widget)) return; + if (!widgets.contains(widget)) { + MinecraftVera.LOGGER.error("Can't remove widget %s from app %s, because it wasn't added" + .formatted(widget.toString(), getClass().getSimpleName())); + return; + } if (isFocusedWidget(widget)) setFocusedWidget(null); if (widget.isLeftClickDown()) widget.events.fire(VEvents.Widget.LEFT_CLICK_RELEASE); From b04e83c72cd53af2fa724d65b341b36d1c65d95a Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:10:52 +0100 Subject: [PATCH 650/661] use appAccess to do adding/removing now --- src/main/java/net/snackbag/vera/widget/VWidget.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index a8fa2f69..fb359009 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -472,7 +472,7 @@ public void keyPressed(int keyCode, int scanCode, int modifiers) {} public void charTyped(char chr, int modifiers) {} public void remove() { - getApp().removeWidget(this); + appAccess.removeWidget(this); } public T alsoAddClass(String clazz) { @@ -481,7 +481,7 @@ public T alsoAddClass(String clazz) { } public T alsoAdd() { - getApp().addWidget(this); + appAccess.addWidget(this); return (T) this; } From 4ff579f56dea2fd8ae05f0e7f8151f28358b557d Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:11:26 +0100 Subject: [PATCH 651/661] add compound widgets --- .../net/snackbag/vera/widget/VCompound.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/widget/VCompound.java diff --git a/src/main/java/net/snackbag/vera/widget/VCompound.java b/src/main/java/net/snackbag/vera/widget/VCompound.java new file mode 100644 index 00000000..e31e1f75 --- /dev/null +++ b/src/main/java/net/snackbag/vera/widget/VCompound.java @@ -0,0 +1,107 @@ +package net.snackbag.vera.widget; + +import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.vera.Vera; +import net.snackbag.vera.core.VAppAccess; +import net.snackbag.vera.core.VRenderContext; +import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.layout.VLayout; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public abstract class VCompound> extends VWidget implements VAppAccess { + protected final VLayout layout; + private final List> widgets = new ArrayList<>(); + + public VCompound(int x, int y, int width, int height, VLayout layout, VAppAccess app) { + super(x, y, width, height, app); + + this.layout = layout; + this.layout.move(x, y); + + init(); + } + + public abstract void init(); + + // + // App Access + // + + @Override + public @NotNull VeraApp get() { + return getApp(); + } + + @Override + public List> getWidgets() { + return new ArrayList<>(widgets); + } + + @Override + public void addWidget(VWidget widget) { + if (widgets.contains(widget)) { + MinecraftVera.LOGGER.error("Can't add widget %s to compound %s, because it is already added" + .formatted(widget.toString(), getClass().getSimpleName())); + return; + } + + widgets.add(widget); + layout.addElement(widget); + } + + @Override + public void removeWidget(VWidget widget) { + if (!widgets.contains(widget)) { + MinecraftVera.LOGGER.error("Can't remove widget %s from compound %s, because it wasn't added" + .formatted(widget.toString(), getClass().getSimpleName())); + return; + } + + layout.removeElement(widget); + widgets.remove(widget); + } + + // + // Spoofing widget positions + // + + @Override + public void move(int x, int y) { + super.move(x, y); + layout.move(x, y); + } + + @Override + public void setSize(int width, int height) { + super.setSize(width, height); + layout.setSize(width, height); + } + + // + // Rendering + // + @Override + public void renderSelf() { + beforeRender(); + animations.updateLifetimes(); + + if (visibilityConditionsPassed()) { + VRenderContext ctx = createRenderContext(); + Vera.renderer.pushContext(ctx); + + renderContent(ctx); + + for (VWidget widget : widgets) widget.renderSelf(); + + renderBorder(ctx); + renderOverlay(ctx); + + Vera.renderer.popContext(); + } + + afterRender(); + } +} From 9453b3ea0577a009a82d4a8dd0ab3064b249d7a5 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:14:11 +0100 Subject: [PATCH 652/661] layouts now use VAppAccess instead of VeraApp --- src/main/java/net/snackbag/vera/layout/VHLayout.java | 8 ++++---- src/main/java/net/snackbag/vera/layout/VLayout.java | 4 ++-- src/main/java/net/snackbag/vera/layout/VVLayout.java | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snackbag/vera/layout/VHLayout.java b/src/main/java/net/snackbag/vera/layout/VHLayout.java index 4db47280..90032d61 100644 --- a/src/main/java/net/snackbag/vera/layout/VHLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VHLayout.java @@ -1,20 +1,20 @@ package net.snackbag.vera.layout; import net.snackbag.vera.VElement; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.VAppAccess; import org.joml.Vector2i; public class VHLayout extends VLayout { - public VHLayout(VeraApp app, int x, int y, int width, int height) { + public VHLayout(VAppAccess app, int x, int y, int width, int height) { super(app, x, y, width, height); } - public VHLayout(VeraApp app, int x, int y) { + public VHLayout(VAppAccess app, int x, int y) { this(app, x, y, -1, -1); } public VHLayout(VLayout parent, int width, int height) { - this(parent.getApp(), 0, 0, width, height); + this(parent.appAccess, 0, 0, width, height); this.alsoAddTo(parent); } diff --git a/src/main/java/net/snackbag/vera/layout/VLayout.java b/src/main/java/net/snackbag/vera/layout/VLayout.java index 34e84df7..3686f6ec 100644 --- a/src/main/java/net/snackbag/vera/layout/VLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VLayout.java @@ -2,7 +2,7 @@ import net.snackbag.vera.VElement; import net.snackbag.vera.Vera; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.VAppAccess; import net.snackbag.vera.event.VEvents; import net.snackbag.vera.flag.VLayoutAlignmentFlag; import org.joml.Vector2i; @@ -23,7 +23,7 @@ public abstract class VLayout extends VElement { private long cacheId = 0; protected final HashMap cache = new HashMap<>(); - public VLayout(VeraApp app, int x, int y, int width, int height) { + public VLayout(VAppAccess app, int x, int y, int width, int height) { super(app, x, y, width, height); } diff --git a/src/main/java/net/snackbag/vera/layout/VVLayout.java b/src/main/java/net/snackbag/vera/layout/VVLayout.java index 957306e5..c8b7e29d 100644 --- a/src/main/java/net/snackbag/vera/layout/VVLayout.java +++ b/src/main/java/net/snackbag/vera/layout/VVLayout.java @@ -1,20 +1,20 @@ package net.snackbag.vera.layout; import net.snackbag.vera.VElement; -import net.snackbag.vera.core.VeraApp; +import net.snackbag.vera.core.VAppAccess; import org.joml.Vector2i; public class VVLayout extends VLayout { - public VVLayout(VeraApp app, int x, int y, int width, int height) { + public VVLayout(VAppAccess app, int x, int y, int width, int height) { super(app, x, y, width, height); } - public VVLayout(VeraApp app, int x, int y) { + public VVLayout(VAppAccess app, int x, int y) { this(app, x, y, -1, -1); } public VVLayout(VLayout parent, int width, int height) { - this(parent.getApp(), 0, 0, width, height); + this(parent.appAccess, 0, 0, width, height); this.alsoAddTo(parent); } From 39eb5ad9c798184f756c4cbff448584a91a12023 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:14:19 +0100 Subject: [PATCH 653/661] add no limit layout --- .../net/snackbag/vera/layout/VXLayout.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/net/snackbag/vera/layout/VXLayout.java diff --git a/src/main/java/net/snackbag/vera/layout/VXLayout.java b/src/main/java/net/snackbag/vera/layout/VXLayout.java new file mode 100644 index 00000000..82d52a37 --- /dev/null +++ b/src/main/java/net/snackbag/vera/layout/VXLayout.java @@ -0,0 +1,36 @@ +package net.snackbag.vera.layout; + +import net.snackbag.vera.VElement; +import net.snackbag.vera.core.VAppAccess; +import org.joml.Vector2i; + +public class VXLayout extends VLayout { + public VXLayout(VAppAccess app, int x, int y, int width, int height) { + super(app, x, y, width, height); + } + + public VXLayout(VAppAccess app, int x, int y) { + this(app, x, y, -1, -1); + } + + public VXLayout(VLayout parent, int width, int height) { + this(parent.appAccess, 0, 0, width, height); + this.alsoAddTo(parent); + } + + public VXLayout(VLayout parent) { + this(parent, -1, -1); + } + + @Override + protected Vector2i applyAlignment(Vector2i original) { + return original; + } + + @Override + public void rebuild() { + for (VElement elem : elements) { + cache.put(elem, new Vector2i(getX() + elem.getRawX(), getY() + elem.getRawY())); + } + } +} From a66da90d161c6afedc0913ac16aa9f96c9e9f2f3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 19:20:01 +0100 Subject: [PATCH 654/661] set layout size --- src/main/java/net/snackbag/vera/widget/VCompound.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/snackbag/vera/widget/VCompound.java b/src/main/java/net/snackbag/vera/widget/VCompound.java index e31e1f75..a1740981 100644 --- a/src/main/java/net/snackbag/vera/widget/VCompound.java +++ b/src/main/java/net/snackbag/vera/widget/VCompound.java @@ -20,6 +20,7 @@ public VCompound(int x, int y, int width, int height, VLayout layout, VAppAccess this.layout = layout; this.layout.move(x, y); + this.layout.setSize(width, height); init(); } From 73267d0a60ffee30ea7d42542a22de11430c2879 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Fri, 27 Feb 2026 21:06:58 +0100 Subject: [PATCH 655/661] give compound custom id --- src/main/java/net/snackbag/vera/widget/VCompound.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VCompound.java b/src/main/java/net/snackbag/vera/widget/VCompound.java index a1740981..c9de19ac 100644 --- a/src/main/java/net/snackbag/vera/widget/VCompound.java +++ b/src/main/java/net/snackbag/vera/widget/VCompound.java @@ -10,10 +10,12 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; public abstract class VCompound> extends VWidget implements VAppAccess { protected final VLayout layout; private final List> widgets = new ArrayList<>(); + public final UUID identifier = UUID.randomUUID(); public VCompound(int x, int y, int width, int height, VLayout layout, VAppAccess app) { super(x, y, width, height, app); @@ -49,6 +51,7 @@ public void addWidget(VWidget widget) { return; } + widget.classes.add(identifier.toString()); widgets.add(widget); layout.addElement(widget); } @@ -61,6 +64,7 @@ public void removeWidget(VWidget widget) { return; } + widget.classes.add(identifier.toString()); layout.removeElement(widget); widgets.remove(widget); } From 106c123f22899519eb240f01bdaff1131d8c8f79 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Feb 2026 16:16:59 +0100 Subject: [PATCH 656/661] add gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 7 +++++++ 2 files changed, 7 insertions(+) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4b76b9530d66f5e68d973ea569d8e19de379189 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..df97d72b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists From 480c09ffaf7df0dd3c5644488044ebfd33c7faf3 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Feb 2026 17:58:12 +0100 Subject: [PATCH 657/661] make getRawX and getRawY final so u cant override them --- src/main/java/net/snackbag/vera/VElement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/VElement.java b/src/main/java/net/snackbag/vera/VElement.java index 7e96b58e..05e7e6a5 100644 --- a/src/main/java/net/snackbag/vera/VElement.java +++ b/src/main/java/net/snackbag/vera/VElement.java @@ -104,11 +104,11 @@ public int getY() { return layout != null ? layout.posOf(this).y : _y; } - public int getRawX() { + public final int getRawX() { return _x; } - public int getRawY() { + public final int getRawY() { return _y; } From daf8bc1f0c1d3733fe7739f779882cec482acf29 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Feb 2026 17:58:42 +0100 Subject: [PATCH 658/661] move/size via builtin methods instead of manual layout moving/sizing --- src/main/java/net/snackbag/vera/widget/VCompound.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VCompound.java b/src/main/java/net/snackbag/vera/widget/VCompound.java index c9de19ac..1534ebde 100644 --- a/src/main/java/net/snackbag/vera/widget/VCompound.java +++ b/src/main/java/net/snackbag/vera/widget/VCompound.java @@ -21,8 +21,9 @@ public VCompound(int x, int y, int width, int height, VLayout layout, VAppAccess super(x, y, width, height, app); this.layout = layout; - this.layout.move(x, y); - this.layout.setSize(width, height); + + move(x, y); + setSize(width, height); init(); } From ea24d4a9c95fc47a006ee78947202fd99de28688 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Feb 2026 17:58:55 +0100 Subject: [PATCH 659/661] fix items having double offset --- src/main/java/net/snackbag/vera/widget/VCompound.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/snackbag/vera/widget/VCompound.java b/src/main/java/net/snackbag/vera/widget/VCompound.java index 1534ebde..bdb80fa4 100644 --- a/src/main/java/net/snackbag/vera/widget/VCompound.java +++ b/src/main/java/net/snackbag/vera/widget/VCompound.java @@ -1,6 +1,7 @@ package net.snackbag.vera.widget; import net.snackbag.mcvera.MinecraftVera; +import net.snackbag.mcvera.impl.MCVeraRenderer; import net.snackbag.vera.Vera; import net.snackbag.vera.core.VAppAccess; import net.snackbag.vera.core.VRenderContext; @@ -100,6 +101,7 @@ public void renderSelf() { renderContent(ctx); + MCVeraRenderer.drawContext.getMatrices().translate(-getX(), -getY(), 0); for (VWidget widget : widgets) widget.renderSelf(); renderBorder(ctx); From 0994596f5eaf5e7ed27e8a5169efb7208aa35de4 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Feb 2026 19:44:21 +0100 Subject: [PATCH 660/661] set transparency outside of constructor --- src/main/java/net/snackbag/vera/widget/VWidget.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index fb359009..8cf725b8 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -20,7 +20,7 @@ public abstract class VWidget> extends VElement { public AnimationEngine animations = new AnimationEngine(this); - protected boolean hasTransparency; + protected boolean hasTransparency = false; public boolean focusOnClick = true; private boolean hovered = false; @@ -38,8 +38,6 @@ public abstract class VWidget> extends VElement { public VWidget(int x, int y, int width, int height, VAppAccess app) { super(app, x, y, width, height); - - this.hasTransparency = false; } public abstract void renderContent(VRenderContext ctx); From b8d633ec56381557b6cdee8c9b069152f82c85b1 Mon Sep 17 00:00:00 2001 From: JXSnack Date: Sat, 28 Feb 2026 19:44:39 +0100 Subject: [PATCH 661/661] VTransparencyStateChangedEvent -> remove d to stay consistent with focus --- src/main/java/net/snackbag/vera/event/VEvents.java | 2 +- ...ChangedEvent.java => VTransparencyStateChangeEvent.java} | 2 +- src/main/java/net/snackbag/vera/widget/VWidget.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/net/snackbag/vera/event/{VTransparencyStateChangedEvent.java => VTransparencyStateChangeEvent.java} (59%) diff --git a/src/main/java/net/snackbag/vera/event/VEvents.java b/src/main/java/net/snackbag/vera/event/VEvents.java index 97f6a54f..2883c5de 100644 --- a/src/main/java/net/snackbag/vera/event/VEvents.java +++ b/src/main/java/net/snackbag/vera/event/VEvents.java @@ -35,7 +35,7 @@ public static class Widget { public static final String DRAG_RIGHT_CLICK = "mouse-drag-right"; public static final String DRAG_MIDDLE_CLICK = "mouse-drag-middle"; - public static final String TRANSPARENCY_STATE_CHANGED = "transparency-state-changed"; + public static final String TRANSPARENCY_STATE_CHANGE = "transparency-state-change"; public static final String FOCUS_STATE_CHANGE = "focus-state-change"; public static final String FILES_DROPPED = "files-dropped"; diff --git a/src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java b/src/main/java/net/snackbag/vera/event/VTransparencyStateChangeEvent.java similarity index 59% rename from src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java rename to src/main/java/net/snackbag/vera/event/VTransparencyStateChangeEvent.java index ac63598d..d5fd41bb 100644 --- a/src/main/java/net/snackbag/vera/event/VTransparencyStateChangedEvent.java +++ b/src/main/java/net/snackbag/vera/event/VTransparencyStateChangeEvent.java @@ -1,5 +1,5 @@ package net.snackbag.vera.event; -public interface VTransparencyStateChangedEvent { +public interface VTransparencyStateChangeEvent { void run(boolean transparency); } diff --git a/src/main/java/net/snackbag/vera/widget/VWidget.java b/src/main/java/net/snackbag/vera/widget/VWidget.java index 8cf725b8..3df12fc5 100644 --- a/src/main/java/net/snackbag/vera/widget/VWidget.java +++ b/src/main/java/net/snackbag/vera/widget/VWidget.java @@ -289,7 +289,7 @@ public boolean hasTransparency() { public void setHasTransparency(boolean hasTransparency) { this.hasTransparency = hasTransparency; - events.fire(VEvents.Widget.TRANSPARENCY_STATE_CHANGED, hasTransparency); + events.fire(VEvents.Widget.TRANSPARENCY_STATE_CHANGE, hasTransparency); } public void update() { @@ -390,8 +390,8 @@ public void onAnimationFinish(VAnimationFinishEvent runnable) { events.register(VEvents.Animation.FINISH, args -> runnable.run((VAnimation) args[0], (long) args[1])); } - public void onTransparencyStateChanged(VTransparencyStateChangedEvent runnable) { - events.register(VEvents.Widget.TRANSPARENCY_STATE_CHANGED, args -> runnable.run((boolean) args[0])); + public void onTransparencyStateChange(VTransparencyStateChangeEvent runnable) { + events.register(VEvents.Widget.TRANSPARENCY_STATE_CHANGE, args -> runnable.run((boolean) args[0])); } @Override

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 Date: Fri, 27 Jun 2025 20:38:03 +0200 Subject: [PATCH 188/661] update icon --- src/main/resources/assets/mcvera/icon.png | Bin 160933 -> 184004 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/mcvera/icon.png b/src/main/resources/assets/mcvera/icon.png index a1dbc980a6917a7fa08d37b993c996059a36f1ba..48341f58a560452708b04bf433be89f79b492264 100644 GIT binary patch literal 184004 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�T?$RFl!{Gq4yRbmH6 zYZn=E2>9j?v7fL)MR3Q6(w@T^ONgW5Kan0+rUTR7og44J?808WNJN{wNB|krzz4ld zTbkGE)(wHi}c-~Z8doRpX6`M0BRwqJT38hgJ^7>{bx|FBG-ta*s5m3{gOO3lR| z_NJWiv;Ik9!UkUdX@d0Y_Po-8aJ&0tSpQ)Fqzrq%vmg!@g)q4FZ5BC+ouwU8yB=a)?3*ykrsEtm*xg`oZH(cMQI{@cVmt_M>h zTL^*GV5;_qal2e5P4pYJ8=V6KgM_RS@SmW+Do&t;vGty3-D~Hg)|N&2grd|tpt1~* z6TZPvV*9rHY+gYjUqnOiPterP1oLBMPfN?6SofxD_oG{%Tx|yJX=iJQ*S_@FDt}tvMJJ91x}$;n z>9QuLxmXun4|9MqwoP)4spiuQ|d%}YH76Rag3u_ zP1-s|$cabp+>`Cf!MzqVUT$%bFW1;(YGm}^^{4|*xMtc}a^T^uHQe~rGuXt?wX%4UaJAV0#qp=SYh#hGL5&@dP>1!!M7mEBi}qvu5>DPZ>;R2nR`2@n z`Q<$%-}9``8uS7E0%w;{GSt7whx9!CR^`6hFjHg7e}r)mdlkBys}A38Emvyk<$;u+ zJ91vKtMQ}CSlkSBerEXVMUo7CeB=6+nOtDewC5;aAcXCMeFus@m5VjtC;Zrhj@Oit z$&Ni*jU_?FgXXu-*CkJ=V#q2?0@XX25uC8?kuN<|gy^vY|B|08rJo%tfFBpkqE@{| z7Y7WpbYedivONgoS5+1dP5EnAGBkYczgw?N)$oWSTo@QV0ecMBbh2vlBVXZ=!3k2_ zmd-v1rc$nq9Hr}hn+@;TIWK?C+_4L7pM4X^e`>k%m7a-UBV)SrhyLBp$Gmp75Ti7H z1dhu&K<4d|Ud5UP{d82!z;(9VghT2@K*THiAEybhs=W9+cpg=|DRzXmF#~JFyKlRg?E>p3U`sM*khc4IH82v{npMg!oP}mI>^9(TzRBq z0Xa#1Eoj9O@buxsBh@<{r;6NM9hTy9Tby6M7!4by@p9Hz(jruOFQb_-e`7lC>gL?w z5`1S9X=#VbMq#9i72Ob8>aII+55^i7)3e5CyHlPA!O<&48q=PqrmqZ8_mDPGv!wf# zA|Z)bL%ijw8e)HbiHSqt>++VN+QSN8p1T7S!m| z65i#0`;_Ki|5keD(!1ax&g+NAdh%bTlG^dj)m(!g(b{r^6LRe7;|o0dl6%h3RAlPQ z3%UwMx?y)mCw60Olym!5jXtiV+WbV@)~Y-u+6=?Buge!*;q}~tTsNo4j(L`WRfjcs z_TIGO@*P~-r|S9FYTxp-KU`k`XzxVWWzECC)+5qzm*!KqZ&EQT`gxPtgH0omx_ z!2F0DLInmfD)NLF`W^*tpa_8;(1?EUzB-XTq$rgm4{0WdPupy3>D???nu-)WyKO$K zB7oD!)+R6dzT`*BScnT;)}Gsm+@=?9oba4@(=`h>sA({-=@$pI9I0qx$kB9%! zvAO;cz$#k`j=3xSOFtr_c;N*8_6T;e!apP3vk6G$7HO^s4!p}WNSfS_C-aNPHAR1U zLc7XvP*KP5uAA!108sHGMNb9FL#Tql9ztZ@zVIGpe*@O5n?-EwM=MZE{=&*Uq}r4v z(a1fkVU3*ksmPtt3gH7*heu%fnd{u5m;H%-{oI(rk1||LnJ)aLoSP_pOjMmh*v>6U zTFByMyZrPacMdx0CeErvt)7S_AOLj}H$lh6(-mhC;L%s|) zzJ1M0D#Wyf&6n!#o?6y+vQpYDHr7L>2}a>2%dy)U{L6!tA_uSt?g_lhB= zDVFtGnK#H{S^QD{>xc)_5Lg_rV!F>Xql!XM;O?9Y)X6j1bFRdUh0W5j-nnG`T2A4`?m|MzWHzpzOG8xSVDIAS`1-P_w%@|p@z1Jbi4-+Y zsX)Uo`MJ>J){1mjM?s7Cd!x?j-&bD)huSEIlFzRS4Q5y_DT;H)7T9?RlLX+#c3a7avSH399K&0)jm?AHRvV;@;EHHF!SQ(Te| z8>Xz~eF4S}GI)=GknWsWqw`&;wy@IK-`_n3k{)wKg;)ZtP;37FR z2K{ustN^7O$JCiy_ae-F;|36#v=dfWLFB0AYwI3J5=$O>mshcaRbS?{9skudo^!)U z{p{@5cPY_c?-%B_22xM2Y(w2M?D6}DK3pBP@>346=Y#19xigM@1tK05x8Ww{V|P)W z`tOEx#cK@}iyiP^lY}#P6%K#YxO|)CxjLA^c=Oz594?wJouX;U{)KJEG(2b*#a=5^Nx!3`q);=D@^9x^ zROP1FLY~hc8})CNgazN@Q#)*>sxkvU*@)^vfJXNFMK(Mjev$VT45$|>29`HmqRSNZ zN-o{*+eH04%mpxs4~LGjB-Fa5@7zQ(-@eTja^QIJ+qPSoQ*`$@uTAYc>3j2vvb6W< zTy;B-?$PhEiS;(L(Zu;c4MgYeH}%0Irayf%C$UOep8ApoCx45bjbkp~i7z}AXO`%$ zVyLc5p2z>tZReOhUl@OodF;7=IASY}l+IohAAw}Fe054Ev^z@XpRAE{EV}5-5vvM1R1I2jQH=FUM6n*mHvNyB9 z;Ag5(sd_Zuk7o30L@1|7?`whFSGyYyaDVO zIOiIYc00jmRqda%f|R!!krA&f0=n!1Ow9lpJHgr>n-I7;+hYGh;{$i9NlsA^!$7(E zeV(bgLq_Y=x>A#^iARE=GXeppkymF`ubkDrxXKi#!Wj<%M&N`q3HW=;^f9Z^%!?A6 z5N+bi4;P$aTM`DXPw9KM7`b&U&%&KoGqyoYXG1;||2JM>GU=u%JaXQ6Yvoo-u$Ae+ z?w?UwB0;!TqIHbFp;uIVtE@8R9^B-3IAeRdCQ@p-HvO%H#Utd~2<+jh=isFG@4poFaL5lTr%J?Y5V}V0au|;BoX;I<(OYbvuciJ00 zQx2bsvuS9Soox`ea#sp{N3s?!QcuEz+BKk5t>>f98GlLN)r+~`RA5`PF_$CB{<6rs zoRlU^dGBcFLp!YNd<#3I+ZowDzoBJ>vX4?MLsGaqzdVhseyy65(iH#s^7fyn>{p-c z+QiGcbu;XH%1anQeF5X6nrW`wgQ55v{{${+S*H=W9{TZ%nr6^#SKpjqQ{d9eq|B`L zOGKu;-2b|GEV0HF5ms9nV{w?D=+vt-3(zs8HR`dBfEOITzZtscc2!4&{^uWo`LnAp zvlMm#KxOS8lF}>gd|@4h=Yi*E)4_sx4O&?deEd=_9=eNUC2l#ay!#H8q| zSx+Bn>{d@6G~xA653`Vz>E2Jed!Ak6{? zb>j>H_bD(}y7enxoZYAxb=pKaNQw4bw1+izWC;0^662K9LU}KR@6;!isTWN%&iRot znT>io@ANr|XBit>aOg@)_~vk_e+o?ynVC<}7R!FLFn?a~9r=3ro(`usk7jcPrcib8 z!>u!WmI9?mU-_yxx2Jb93FSmL#?~*~S%L)@9}C2+}~okfl03@rv5YO1&DvfNe*f=CDl+Yqbz!;KS%aPw!MPoI@5YWZ}e zBqzP2FJtkk4szcbyi%|ZW@KGWjBr@2%jLBGEDA?xKZ~}Zh(Mtuo)Y&11q6Da0H9qg zCt3;kdY6pwpyNOjujI``#iElg4+V;C7yR@b>CpW0Y4HR3;Y276tV;(+@-#^38%9$Kz2Yr6%hsvdREOQwO! zwBz-JU7n89$zHj|sx2SSNzU64ow}O5O_P$mvM>7L)cy)qsSJd2Jyf}C20ibr_gH&| z+_S#>Dl`KYT+K=kU}Z?$vf6~8b3jb!piN{xI}3BfbPP}caDKoU4E@azz`0rmmFxbu zwb*9T19>gn^O6Zr^kZ5WF~1M9pw91ukg>PmyIVNJVSL*HND1sgybQrs0s$x6Y}~zW z<53ssq;9w-!KKiN(DrG$$)}lm&z^7}PyyVp-6|Pe;OHSO&;I*O+ghLU!-oUbD@6`O zWyMc$F+t7L8|ko#S{2S226I4WiNz#yuU(zYMJcb9+N>z!@$l)9}oyW(U&H`)Z zd3Tcq~RO8*&NMvBr5n>|RR-P9yGr-%ffq<7|WgDs10F66%-aR19=aP76(>mUqg96WGz=^$N5diKCwg5CrS^O2!@Q1cEQ(J)uVzE`8`$Fc@>@jj3b@pJrJRfD}MPY_W9VQZ!F&dtHe+7fNtuXOPCu# z1j|UL2RP8Z01%!*a}d^B9fT=vw$Md(fFg!T^(zrRgn@*d^Vex`W7%xZRh)t!gF3Wx zTiX;5q_%{2nw1p7C8xqY;jWAfPgg6aop4E_9|t8DYP%FIGq+QZcE`oS%-r#At{=xx zxvGaXkBr{#u8TF>i0D_%a{c=FNKo?hrz{(E?LvCRWwx%7jsk$Y{$LZs=x*T_Qj+G< zKNM$7$E#HaxYrsr^WcK8&aa#%&T7AuL65}e7uwB?^f)nwe{K{7|Iw{iN_(-u`j5qf zQ^HldNW9fq&K3e>DYHmw8LMQIm(e{uO6*OTi<WCz{uK8mZY zHL?p3&jTu&xXoUC#|WMF)bXQSTOI7}71Ln1?Q)THxtNAA_LqSiAKW8T~)9d0yo z`tVZy+|!)mILy`p^pKCaPo?~+w8b|bMknmG%D&BC6Pr5%6|2^zuR6q$<-s$OckA63uH)DJdyqH)QNd00=Jcs@ZDPlLe-{J$KSq|?h%oiEb+gS`dqgZX(D^R9Ip&MT=sxG&B7Sw{p{97`m;zOamHs;Ux9A~e-8EKwY;OMr+k!r8l?r^k@4 zoAn661jNK9OpLbUGsROb<@Xr`&z_;B%j{{qi-qxl$A0-x9z3BOIcA+pRHIVAzSMc1 z{o(Agli9%U>wt7-`|Fpd9=lW1m16SyhwYOOhBTU<<^J>AbF>~fiN4RB93+47JL2&J zdJT>JbJ)@WEK5Zqv&h5Lwlo8gI3*Q|{E{AZL)$T{^mNPq9;4;>O>{p?mbUAL?6KQCf4Ee)`6Z?SJ(xxmj59eoerQxRw1sl5N<5uw6&5^xUrZa6|tLvb?6N2n(_ zoysw%6Q_YU`cM;5@i7OBpUAm1Y^kKK;wEH1m^@;9qb2!~=Plb$RI5$%qiF91XyJC- zB(@HhrMC6)dy3x2K&93|RiuLDVD6sg$42e3pg>yNoayI)FGus|s$i zCRs3cRwcd~eR$zNYiryO)05}?FpR?v>}8ZZjDaz?DD<-ne(3K7Z>w! zWW!}rQbBI?l&Mqm=KXl-qn|Q8x+Pdvuat!{5p&zeWmO779V5jWv%=VOcx~cA3UqH#E3%2(S!D4 zWuV9qE!WMjSLJK0!d9wWD;G}OQ~sU&h92$ev}ZjJ*>@{Lri6GXbBgS;7GhbB7B1LK zy5M2BvF!&xuDoUx7sQMY$o#10fqB0Yp3RNT`tciH4CX89nlxz~ceC60*4LUWS&Ps- zY(pss=%@|k6j?P*&IZnq?Ry>W|JHQ;byuxfq45bJ%rS4hbEGeDHiqt^$~}nh)!3}j zzPpNxCwD!RyqwZG>j|$gqM+<;y>8S)Da1WD1X;kTAS8xZB`);hm5a!Hh~XUEm(^|D z@Y_)~hWN<6`=Bl*(#Fp6hqz7*aMZj9p}tsrR~jv<@WJyZ%Pm=|6(6<`=Ss-~LG zFwXt0{0|o4tN=s)gOZF9r%v0|ow_xbv#1y4I>~GY5^YhZ2E^a#7a0vNt$F|$f&RJT zB25505fN`X@I|&B_E%2Bqe3D%1IpGz9i$B}$bUvwj8@h8qa&Cb5vxiub3}0rkkcs; zmran-Fgf|~lCCRO`-NrMxKriuC!~b9*T}2CT=v!pfNgq_6BRolZYkN|=WnD9{cCB0 zGgH+o&f}fme{vfQaE0att^B^ldoV(8X4!oDOC&xiLp~N0xFV;y7O;RUF$PpVxa;At zV+nIDWO#?oN;CaW@i-MQOqsn4lV4)FU#LBASX)q8Il<#P`=J7Jhvhq;ht-&H_HhkQ z)%WSQ_t$N&v>jAHKjl>VIAL1M$^G|!-YrN%b)VzLY@_-MxtQ)}oO|sp zPW?%xNxHhLqR;8C$yVH@e=|J;1zsA8@mGIv!zU;eE+_ds|0HrH$nP?CU;%*&HB`W~ zUS;Ak3g9x*eS~yRJ!0dDyrk%y=59Ol#DcFwwb~)u7sy&lN9XuB7F;{2F1J?23hQGX zd4KJc$F=l5=O6sF3%Y5zlE?rFLuPSTfo3@gr?T+do6OPX0rE3J*%vE$*w`&#%c!-O z95;ec6>=`JS;`?d(klNMg{%P_^~8_!svAD~Q~!jjs@CQ_#sjHSH?hWlGr0c@8^_wI z?!Vo-af)ii&*jp|Te0_0hJ%GYg8wnN9@}42y5O<7-mUUMG4H&@k2x$qKg<}I{mf&6 zV>HR^n_J{W)bOQX-$~o7*8?lC_#QU z`rwP1gzJXr?cXExu|@Glq!fgb_|Tn8BDW$#@OpXmO?MB(M)-a&Nl;|I>1Pgm$~~Hm z7A}EVcySg$IoY9_WqgD6u;O(qvvL5|r%En;;O;ZdKy&8+b2C9X3HSOtt4#<0>>~s$ z?!n^F$VNYyj=XUvf)fs4Z1ohl z9c)q^Su8PgJ%+l@ul)K?BL+3K26`t;Jx%^4vWoM`rBPRd$-fg?OD|Cmgnby#nU?S9 zm|mb$CDI2FnOPa!_RmG9UNQhOz>nqs=+j~fa%umT5R%bkf0>gJlv@(|!n!+Ts&|*h zls;FEdEue4`AeJ*Z|-qIYq%`!xZj@u0VUxb#C{L8w98<({{ ze7d1Ls%(?_SwwhI>6aJw)^D@&IL>d9krKhYicein`yP3A#jdfiS<5FBUK3j~yK?Pe z6c9yA7}zu9`XTXTf&SUhsQLS=i4jd4e3zbXF$>hGD$EE@UAqTl4y^XK(06=SpB6%u z9r3#yJDXV25}coF{-V*0IUwg_&yd<9?xD_HqxWbdX|X_RUe|XH=u`61SS90!x|u+Q zq@*vGMQm&WYVO>4;vVICbbT%O31UCf~MnJ1c`RF>)AcMC|H$cqVaQg0ZKvtdbQbMlRG# zv3`POQUXVI&vwk&bJcggE4>t6dy4gc4j+4!`ulb#YQtgcO@H)UIIEr1(S5Am*<*UD+M7U_^I@ zZYgdRvGsxH)_tJQ@`aeB5VN?u^m#+0Z$Eh5V`CUq*mDmU=DiyKP?KpxyK?@c2_>5G2@+mR1T9h;S)dQd9y1^%yudm_kgQ6wz-@uVq9M)NmYKA*QCNN|B4L4I zq+tDlL~X&>u-tTYzLDXmX!H@Tbehg zvkq()jdI?VjO$EN>-S`zdX@J`F1q`oSe{67rq~W&Q75_k#40nDC=QMBSh(GAXEw~d z7Uca7pHud_=PqOajaVJAyF*Ti5)&mlRhxD4M*(-);+>nGnuk|Pei!-jUMFnV*yOPu z`=gs8c^p9QwnCwuj@oIr)hfn*p}d=&L$$9_!%Jcg<&+@5zKUIdYH4Y#3*!@UKzH~R z@8#LkM)=5lO6hn~;YqzYpkCvB6s(J+3;85?>b6y?5X;1@k##!qZ5yokGRN*9BeDa` z`XlLOSflA-dr5*xXN1A1%vY6aso8dS`PQr*!+|l!dZCBkUc1!vTp5?*6#w?^{{T}! ztiReP00^@{l)+_-ia!-6W6ZQ#PCKK3|>D>vc0xNthpU4V~OCbIB%zN#UH#2p-$6{s;FL5S2f3 z=$W5n;39d7n*;1u%mAoM0eLhqKq%8*&S$)4O~S%v05$>8GoeAULV$e%_OcRyXg^;R z-cV?Faet8|g61HoD+9izi~Xymiw{sm`|GELu3jlr0RTPRX&18=AbbHn2YUZq-XA~{ zF9-Y(zIC^`6}QKA7{s;9O8Gh`*Ud1%XAKj`HRi} z8m6jQ!1*W!Xafaj(=r`qX8NjfW~rYYovOrAl|tx@ePJ!YQOaSCe^{3K?5-X!p_!6(`8Vn?NN3U$%tKbc$*_j4;UhEmgT6bvhOFyv8YusQ^(3ncm?S zumTw%Y=1$TgdjMaHPV}4-z<3qKuGc!giDyem8e<+xxQ0rnSp`2Y>`Pb6Z2*9Cy@7-XoPAa_j3W6Khvy|6DxbkJfZ8~QYMKdu!s$l zw?mzjk&5>+>vX`h0#$=hmHfQoZ_m{)2aY4K94ax}S4viYm%Xe3XXam6>&~$RSW4PO zT_jcMzHsvo{;7AW%{iLX%sm8)?vxIq=Jy+`&zgBqOy3dC>tGb$*XyY$mAX68YeKBjD#rmYk!OEcL{V=nWeAyM!kD5ym9 zJEW7Dz9Ww%Yg8lxX)TwD%%U|e)^RA{h~KJ{HtKIWt!0@Y>qeM1Ei&Jnp^GL^m&NkR zd{ReSar_fE{n_IIh=)|c3XJPms#?`TM&D>r?N!xu;lZ?&EJyLm7gqmY7r}!^_64XC z|1|H3ba4{GWmUAvLXfIt+SMs8Ar^L3X~0Wp&+Ze)!8qIL^1!Fn%5}~mwGXet<0n-+ zV9^5ij{zS&daE2NZTxrm9Nu|H;sPN10#s7~!r!(z_z4TT@2WE(YXV?H6Yyi1zm{=J z(#9%)Q5%4x^q-ggdB7j{{P!h)u@b;k{dujwJN!8i;Q5CbAV4I2bMTuNJ4SWxV`a7c z7Qv=RT>>aJrXAZd1zr?g{-j!h1!9@tqINgAE}&p;jGoC!eQN#?nW*(KXME3Tg8j)ZS@ z=~ddGs(Cxkb;yH3iKJFjZLN@AldrjmGb!Cdm%fb5q@6(2o+vZe>gvo}j#WZPW`*P= zW?BJ1cfiEXgD$~)H`3G`zb0#Uyq}4Y%0F(Ea5b$t{Z%5iEa3pC%LG9r877Dl^u)9T z>=Y$Syo=@&hrt8eI+>L3+%7EA%)}m(#RI13a>BWq1!P}>YYACQ9$wj(8OnEz?P0s^ zmue?lWXo8?uVG-1<)BWn=FrC&_b5yDjxyd+4fH1IgGl)-?YCupsHQUUbD4E+FhieY zp2;)9B6)1^N^S@62oWZDHW0wtrnxvTS0JF$d@~VF{#o_=2vB4#f&e%l?*=YTYNuR> zmNZS!-Wf@>15!2!V?#5W`53kE&!|#M2HiRNsP<#%&m9cgHRXlP?=h-m`NrZG0|lsn zTCXKApbS4hHMJ_4>cIJZeUQRQlOSe^4nhVSQ`bH&w-R2F|K(ZE#s(`8{fVmb<^Cet zF`Y&1r!$u#TF+S+|D<3+j#+ayRUp71p#tYJtABu4J;m#f?}=ysu~ZPc@Z6a8JJSS+ zeZS5-TCQw`0E;*rIZ$F`oXHTW@4L6KHh{GPt^qhR4FGEbF6!SmV6g!rK+vD)&1b)q z06~BJ;~%RT;GI|&_&4X;E35_h;#~-5z>ln-SQFrmfPd`(z>nklR@A>O+dnh+tonCm z&-ZwRhckR%>X*#`V(&M#zpDD<*ggAMH91$fC(_EYXnBRXUSQ0*po<~HWdqG-v*V3>ZnFn`LUZU@cFFQgP}PC5}SPG8e9vy z!LEb?R-Az-6Xvp+oS6MrLB>Q&M}$fc17(o_(^)ND{~o7Eh^V26Q3Ez_PA;NU(1QWk>(0T8-+ zKs5(^ejqUoc!*T<7tly>W(F`oz~%HC(jDNydqnUq7QUZDQunvr`zi?FKt3GT#z1#D z9B1~@=kUt1#=oeSKLY_2XIk#&I__~{J1zUZOgq&!sLw!S+VwqkUX(?B0YLLwhX z02=ew-?BNtI?rM!q@52z$TfN$jNqzz-X&rID*{yVeYLZi9bd}P@2~0x3PxSZFm{cZ z+xr&-8t!a2041w}OM;WW!wsN*-xMVcjIh41w16f$#MH0=dQG(q`hGDrP=E@(UfZAy zoy75o8m=2dv5Y;QUwF^GtRvxV9^<)hC9`o#h5Iu{*y6Y1%aWNQsKdw zy!ss|5Vld};1BHLl$Pn4(cZ?8(kub3#ot$)j*Y*tbg`Wfe_SNJ>Far6{{0swWFZGH@sM9mxZ7}1Aj zmsjZ@72xRFel4fol$j}G+?@O>9^}OU7Vw8e@)t+iWcL@!!OXnQ2%aOntr`dAn)Y)+ zU0TF*gw0~h;rd9sX+8?$Cws0QUmjJ1z#w2cJ6n#2tMUNPC8D4(f8M%m;ths$WdJk( z#UcfAkRS&DmyfFjrUHORX8UhifMW##za|e4%=gw>fRCh~*aslT1NbrN*7yJn%-;tg zq-$V1{z=NVe^1W*{XA5CRqg+5lChds2w+<4Kl7$O?(nxv*L7r^O|BWBMO2$r?Z2BV zykqjiQ@s+k-OS$Cm5$7+-e(lRGeta)UHYFJRkc8Y00BEXI5XN2mhh@MZ z#Ip4rB<-$ACrYfFurn1=SE{3hzhiRMzNy%m>P#G}eA4d%A@Q@;Sxf-60F(A*S)hgs zX0-y_=zJs5c4o3zoXwFi|D+)wb(s&hO2Jq#Q33%Y`SSXU7_u5XFa|s>-uZi`?;$BWeDw6heA5fY6V3rCO~Z7>H7Yn=e2DUo71sy z42EU4GAGxD$y}8$petg;9yZVF=%>q1C-pQJp37o4N17B%gE|gOlW|0JCS3W0j#a&i z;@QWOSG8u+%p)jDCtKnQW)hzSTh}?rCgq~cRu?A}GA*8r50~KO6_g^iD6_(1nuy=WZ5gW6$a+gs}9Mc5nzp^jqakZ7COK62_EV~bG*SUXox$%s`OEUo6n+= z5{Z8jrau>1bjK?G%D@*{jYPK6sNQF7z4+KjF435=W1o0CE&Qzn7R)>G1*jdui$jQ2 z_C)63!^=f^2C{u%f3{m>MRFmPr$~J!CUqpB^NTYSFQNHdf?9F6bA-VC^<`xr_kv7_ z(d<&paxnAGf8PS{t4-gX1HMuZA78@6Ab?i`yz|aGboJ!-aPjE(_|KpHy1wx<{D-&S z-C*z^g1Z9JPY?u%)2#piN&TM(d_LdY@sCqp=9jN!$3NN7{Fx4J_tLjm{&J1&{qx)$ z{+Ri*#Iq>#uT*Cr62E8)RP8U4|HOWxbvU@XAB5!BF;sEmq>fQjLDd8h>E9%@VP*zB z862K;5*bkgVW`t&E^(~9V#Ybi0;?hpfx@aZIEXD6;=8(dXwv6hl916NU_vC+LX#`c zjg0s-WeDwz)s26mVE|=}ObI#9%~O_Y6KBmBND(VXbe75DH2Pw1vZ9##D9NRwe+^;-b0=&D&`b+tGp0Be8jN2;ZsV2=e1Z3ct2OWuZNz7T3plRLX4MW>RHbThe6ElAhZ~>mD zsFp^?=R8j)8LQb!Mo9^ljJm5#fl(ok28?Y$rDL#F0A}l+11{QZGcBB{yCu61lG;>v zEL@-r7J%kABjprPQIRxFCb4iXbppoO-ZksEbyzMy#YXwDX%^6cGWtC#t|2qD4a!X? z15AP%Vvgy|SVVu??78alsHR6&TA?iuwiXDk33`E|eL=SulDX&s;NEqPP^_=LmVq@CYQ)rV$U3 zoi3GSu)CD2I{(jktswj3;1&C!O-+*|dJExG-vfO25T0;^%O`+u?18>w$G|!^zPteZ zyJv3;SOEBS{rf#UeB*ccH|OsTHXlum{p+_N)NcMUK;SP900!HCZpf6g*?nRD5^8eS zn7&K(-zG3%f1>t(h$0=dc>ZZ4AB>26*Y*62?ff%G^b>D?AyE5Sock)ytMu#tBEkP= z5MsnqHcW~uB#aL;bds+zp<4V|%15z@iJHEsn-d6InEWvw`Ka=)G)H1qi7+foRbl~f zKZ%Dek7`hH6Xl9|H7AXtI`ksA0$(I9QmK8hl>;cUn#y7k$)_YHn!0|8wLywj7RX9| z)lGxSeppOcf#M94WI2RLBt^BT%o5zOAyZU)E2@UUROh1qMKMPpI%H@{9)Vl5c(*F) zKoqAUoHRxbh9$`~k^rtgF{B})M!4>g5H^)lh%~2aUnV0?MK#jeEdmf&fx>>v?sPSA zl{p51L1nu++uvib!}on{Li4BEBe{ zM!+VoougTq4C78ZCRJo7w5kaZ&`!|LIYZNa0QDqzve>$d*;2^6V^JNYY1|!8kV2d@giOl<8sZ;_j?4eMnwY*tgK_LD!0xC8u4z}!Di%Jk zmal3 zsg+D*A{%)crvglP*RMf_lFgQmwj50JNTV%}c7jRLCTd1zS^Fa2n7p5uvn@?>^)HVx z(;J1b*UAL6*K{3hRny7mH;FnfJH>kuA_9-TOH}TNR{7*!i26`?@?j|U0Wlf=P<0L{ zo<;T^G-75%1+^8Z%y(ncC^WT+K5UK9QpnMv-698B=$Ev;;s>DbCTo7{T#AmfZ=%Q| zdeZYNPoz{&{TlWb)f`wyV=|faEwPzQCLb9=jLAUlTQew${wDhl=yfLjRHBuE|9hPl z|1Y)~%(`g`@$x&fbLu`f6$BKCD!3EtPoZPr%H6cfe4&!zBko8yr($P6o--WlVzMeu zv##+c*u7o3%m3BK54K-x99kunQ{~}hePumtYO!EK-pP9m(RM}&p6+z4b-P)j3&6d2VF$9huW$zWnxs6B6^PWy-A zHwHyXW5PSFX#eoFBHndlU+I)xAWg>Tjs82JO=Spm6&K9FlUUPF_90`cD9QkI{T3zc z?d7`a3zYezksQJKS_V4Qi*I6XNx|u2nvzO!M2-Y>av4Ahc&#K?zjZMg%L}skB043m zcpcM!x`yNbhu6ojA^+@rN7q+}nbli|=~}i<=k;F8bX75c*!U&H`|(im@6|Jj=)(B> zg2Mn;?eq0F6$HFS<$QhxA3p(jlOq^D2K@3-1pw7V{fo3g&_BH^I|Ra`e*ygD^LpW3 zT?*I*1zJ(TmrE1efvPgZs$p^bL|J*+L7oGX*Nxp591eKY}gaz;@VboOC4s&~x zi93W)DiM`qDWzHUEP6x?Wx7LEJLYc}$KjUq;Luo1*V2GBN`ct1R{>DQK{Nlhj=@~Y z7W3~~t$Dm%;L=}uS#XK~1Sz>tdoqpnAg`oK_Gm>zBc9qGBc2dA0b3RO@)gMkIc8_cPRP+{lkg2sv4lhQGmp>Fo{03{#BdjB%ADrNB&OzUhH^pf8v}siH?W$ z^Ri4&o=Nva%}-)Q*%iSSJNCGmG4+=yaeU~Lo&%T^Z=7*pM$(!Ikn(l%F_v;RG#s2I zIZF8)Fwkv%V`Ih!(b4M+R2fJAD4P z{>tN+eEb14+^Wo4(V#rDn&Ak=T<(!D&r}+jfAqJ}sN5E^ZyMsb)X{LNiS!iXSo+?_ zzS2A8yV78a_F-SvR=biqv%&_x| z24Ly~{($FU=8wAHImkr`#=EU-G{V+!gEGJ%ohzWwp3PEfBdi0nPS0YJlFclO9V@o` z*qYzJ7E}G92N+!)aazrjcF!?R>H{BB@qQ!nX{^aN#HneWf=O*6jO}-Ot%JEy6V4+q zHAdYPLKKRa)&a`+L!Ec3M<^a7r+OiKkrhS#Go&{7Y5=X4Znl3`-R5pmhRuHyN8OuL zYG2rK&zLl=<8boQW<#6;JUfw5Vv$6rEJbs#{&-fQAL$y(f0Z1F0 z?HS(C!N!dG31YP0%5$l|4H6#*@d0c;Q(rU~Bh2D@(;}$`$-c3Up$$rgVRi54fA@}l zykG-vZKjXUsPPx$-9^T@k9B9(&V7Ty6Vi$KZ?&{%FU?z2& zkoEW}wvB%HQkMSjA*jlq2_J9cJMB%{7pK3e8gXXv@((`&^$9LEn4BOuQ!eBJ_y=ohIchmW?M2&;k*@33Nodcxi9B~;@C)*(6USM?NP-ic`VL#ieI8Pu{R!I+ zvf4jUc79K8*DAHi>N>%J=ep8f30wv&D^rq4ZrBKcx499$Xu50fw;Sl70+QOb5i3kU zq60Ni9~wKXz0)-nt3INQHrW8_YD!KdNi!4W$6#=|Via(ZEn~z7?ISpLh zLb|S+{o!W~YA2U!_?!^PGzW} zu@O_ysgdS=k=UBdgRY<2w~2WMp5OJ}c9@>;=7RMrW^SX*NEr=-N4Z!6+_mo+s2&^Y zXi_FRSdMv#fdabuero=Ei_#d`od4}93cAzH{{Ttg_AqNmdLBAvi8)UaoK>q5cIfy& zzC+eRbnP1QL2H+?ls_2&;Yn5uRXReYX4mrTs5VA&yBW4g zXux5xv0438E2l)AOkIwE&3r)Agr<;gs8{8aq4jhvOY@lex1U>f=yPK%cO$uRI{L-i z6ZISO_x18?TWOOGP*dCDm~)V_$Qbq-#9#mktFAW{$DB?vVPxck2BUjw&_cwwG_odr zJB8%g*HI__G6@JUy?NI46;UGq|^GJBg7VCwoX=8I|9vX4s3ja`3Z zu!1;n=1)XD8MnTr2I9O=nrayawSCmX>WuEZ*~S9~@X*?H^BFttVb4j2k;I!+cG^QvpXhS6GMx6!GwF0T`7+{2wIi-10gO4uu{TE&#rmdrx z1I|zbPq7)`C<}gYl3-YsA01|00Wi&4(uFJuynxGH6+>kiT9xSQe^2+1k(~i|Prt!^ zH3NKCH2`0J1Ni5!wLNYIU_Ssh1BfGF>Tr$)@X>ES>NuZ)m`!^5ymTv^0R0qtg1SXu zVO;xfV8j2Ap9RYGO!tC^4t-A9*(eHVO&*DE zCe+kuv@@%%w&rJp**XKuQr$|zAI`f%o2H}wl0TBre<+*R$c8-gB&#s0xgRGBe1#gmmnb>0Q{ybr2uiAb>`gMc zjq)UQ-G}L;wqg^Z$9~LcGt^HblY0F%xjM8E0j}cvxS*WCCb&i*u30(#$SR@fY1)m( zipivJ>?-8*-`>FtZ<@q62+bxD{h<9SXWba{_fm5NOnsK-KXh}5h=!Swv`^H1JZo}# z#u`Bs+7mEC6(lo5cv>2x1?)PMQ}PulPJgJUuhxOo77j@_A?gexh_+eE=d$ zKH-ja^?%VA=g3R{IP7m;4Z!jKwR}D2jeas9$iP6O{q3it(zS^3FIoT&2*J*r5{yDO zbvLq1iM1t7x3{Yud(xTMUjk^FfixK+BM#3;mk(V42zJ377C=sxK`DY@D|NzZ7E5PF2)w1Pw%t?nDzF=EY1R_&9% z4>mZuu{knujDLsnQ=WX(w{=2-m0whZ2xq4%VYie<%)xIGMv=)c{Nq6GqBmr@xsa zTo(z-C(E`oVa;+WG{O${>6_vl06Gg|!?WB&7hyc~Bl)c%TIr|cUO^P!B)Ci*$g z3sUJE^4##V@ml-1v}XRG(rIQ)4qG$}CZMq*DAqyVH`HFE#rsfC%%32DuFqj_n@QMB zT6QP9;$#X!QqL3K*Fk6I3N8Vqwj z^tyXH;~{Fq3(`IJeTe*W%pu=7cEBQ$-Nf$Cf3+6#k+-Mj<>YlXoy*=BRZTsg<8>8K zuMhfR_H{0upIy+sv-|XH_e8GEmw->szQYF>MEmovs}|rZx&C+k*Ohnxdh{NQNi+c6 zQ23de01`ouORz;uQ`*}3Z!4wyj^=SVc7R~!0Pz99ozi~`^Sg_1zt|R&cQ)iSfpyMN zcP%91JnnL1t&E(|CAbxbpfw#td6vFbpDKTckhB1U6Jhje zknYs;*g&KcElQHm@U}25v=$jbwVY)kcI*?i{Z6;lRTaKzxky(A%nkrhcJNyy4!;~v z_E**XZ2x2jKN$iB8o+{BF}PT~Jee~dY$DaO)jD@KHdcn~Ji*j3X-r}mQSJjJ^w2Y= zZr)*scp-y>T)m_fOK)rQztzzZWD^Ba^hyU3!K4rdD$pOxg7wC@tS;$TSFuC*5u|>U ztJu`*#^^DRh0Nr)>XF(>(eeSPV8fr3L4-@&n7#+3Ny1}V_9?JHiRrBhN(x2W^S9Jz zG4dIuYxuW~4{)Gkz^|DX()af|>xaQIP$+q=#D3wi4-$+@ccf$!n1rg9xQDXMwFJlH z4YG_c@s`=se8o{^<@`|R)1^3@KLGXv;A^kc|DHV4;dOZMhP?jE@2VN#0ic=y0KWg~ zH!GUrNwxzv>i4pY-_u{*2Y`CD_DQBbM*kCD_e}!>gJd7lKU~I|e>vxM|6oksHutgT zB%&Fw|3}+<9#OaOHLbvG5j>D^#Y_TNw)$h|jS8j))H~RGZ#WAg($-f@OoVPAP?4Rl z6|<>^cEFj(kQFiu&y!5gF)<{{Z31I3(5v%ve|RIEw|2k*V89%yey21-Nwa{t{rNsb z#iAZ0pS5SiA@L`^pB?T}#fJCLaKC2^xM~+PZH3$D5LT>P!9XjBuj7x6N$S+TMSv*(rM_R#`W!L{B%g0#KOUHeeNXH`141_@ zf|>g8u!m#*Qzpy=4Y0QwU;;8R|MNV|?N3*_p*DaJDYul6nw<=8f=2<<*5V8<8gq2e zYNe<9=+jJ(VzgSPG_V7<)UYHcJ!%@BPHK0P72+_1y-(2W`PBJA_%Sx7?uwSyQZ#K% zfjscgX<(56D-9?J%8)$>CY0?yZQE*E7WVs-C4p0w{3RkbuL{_yR$w|_^UAVn8oR2~ z;heWa$E*2L`4b1Pvk1_0H3K}YT7XyKRoI=OXaLv`;Osu&FP^9wfb+2rfTw?Xdx!y$ zQZqM>H2%fXZE*tJOeKc@%M(q0} z$So!!AF4{b1pyy$W4MM^qk#eRyy#{m>>SQQmkO|wxVqLy1}CB&(2{s&(7Fj#t5q2~ z#~OBK?rZoLOwN2LegY!xi>jZ0lZjv_T7p?YLeX%)3%SpXT;K6-ed1)Wz_c-mGk{)6 zoJ1X7|HNkAO`CS*74*nZqw;Edki5B&T)pEu4w# z^BA=t349svR9}~b7p%+({^M-jt8ag)jG&i>Uq}H^Yj4D&7}^u<#!=jzy=b~ks{nGk5zRQm=jDH6-2c)0f29$C z&;+zRyrBcfCau4F&Lc~oW{vvEA6P>9swq_}FgTQ8SY4q*#7>9;k@nJsdbAStHjv^= z3mZ?vfiOYO=;EHK{1O=&t*xfYqmgV5Ln%$dLF(-o6ZNY9P^+c7rL3K%hsLs8JJ4wr zVy5>g0gkOf9|d`@kN2!bO{0*mLXRII-7#9)mVsdq8@=8$Dp&buNznT>IaKI3ah(gL zZ$qO9h7FZOSw`xFskZ(=$V>`#dl**7b5Qgq>6^MA&HG=~``|tSvh6PCm3aNdH@BK) z*_ zb?0by>w&2^K|OkqFwG5PMt+<|cIxfS+~Rg_ZJPha@Uq`QZo7Z$m=`58=TL786D~PG zP-2YG6kU|+htBv~1%cAHfF@Bx>LhRt2YN?^sUmFkTN1{)IOfl2rg2@O&p>u6ZTEqB~b& zxt4tu#U#M{T~%g+*A)a@yVm+CtP%$VVl%+!UwkvYa#hU$zl4+NABTWI;R6tZ|K-g9 zw-EpYv&t8@cK>rAVEWaO(L+`L$2IFZPEJ+y-D%Ljk#bmmuwfV=J;fC*M=N^J6nAc4(ai5EN7S zlS=o6ELi}Gd&42+4#HLM#6 z-_YaGCUMWCGV=@*3qfb%Y2SrfA5tm{Ak`02Daz-ahEN9B0bC&FYCr#*FyM@DrA_GD z2Lv1Q-&Oj3*5(TlTEa5Se`C66T0wNa-NnHmjsrBn85HFHA+g1Ur7(d4VaQpw#_N)%(@6tYxAC~^p{pqS{ z0VZeuSU;Q6k^>%(2VwY&h5ltA!0+cBBCrVDz*0IVz#!v!t|s+7LGXK^j8IKNgKYv} zJ^{YoxGBzPl@S{8_d!ca9+T`C(s#@=T1)+jNo^)-Rfm(zmNM{IUk7Nu2wA;tx6u0t zyLYZt^IJfo)${bwcce_2+0#h=as94A^Dz{vOcx=`o4qvRzu6DLz+ikgFKff>Vbote zh$ryEuCDgeM%UoayU?|lOSV7h19-3VRcXQH>$Ef8l11n%gEx{R=``IminXL0x| zVFK`wz_y1P+V^r05KPoR^UB->Jviv@d+&rZ=+HCNF%=(qJ`24O^FUEflAoDzev z`fWYa=D(}s<+ulMz@DH9#Rjrku;F&L`@7Vl@U>lk;a&HrR-(ynq)w9z7P zep*`iae~BXVX0(#drYPvPFo}2#Ss{n>ecvJqY0r+1n;QZXnKLg&^oCE&Aey`bZCtYdMKhC1PT&fTxq0Eig>Bd(W1PFYv zV^VLs)Hh1}Y8Na0IK;$=(`0gqe=YfE6{5C=LQ)uPrHOKNb&J0D!mN`Bx{|FQd%I$0 zib0*SHX@i%Fm{Uecdv$qmMBgBHoJpy!%(lyF+*LQyDB!spS3pzItHJEo|w_8_W=av zNY8c%JQvv{^ML8teS0Nv`7PbJp1h4isn(Ez?&F5a7g=M=`z&X>`l{GDvfxNs+@G3w3v5KtBJY8c}x`G+J&9 z!rXAHX<~EWS_#Ny0AO%SYJKc~(PI=+zxyFrEmIzJVJ2uvGOMKV7G){WgH8ytf2rip zY6O9wYo-nI_WCyn=HAMIPK#pg2egjq6lC{ z*~4|x06}gB;9^)1JuDg^=rF5Kz@aLZuUQjtWTJEdSJzMRoAP85AAp)TAP{`5mHqDC zpXC~B0NfE!VgY>i8JtW6KRVsENPs>m_2YahYJ1{93w-Jt0H!34o3aC-+y7SuV|f4T zX=VOG(2lb4k1~Js*a5r}K)wGQ>{Btp<|-axu|~|4*~x=$G|i$%(Rd|iW6~a zt&)89ma<+QLCG6k&UuGnUb@F-EMC)0lWu_Jp8QRMM_UCTeJ6VbuvWmdXe`~7N&?+f zAX{3Cq{_= zQ?Ga!6=}Pd#<8y z>;3Hp4fPSg<$x~vLUeVH9`T1vH^vNplK1kwAe$R(2Joe>90-fe0m9ui)OH>62ZU>2 zhceqB$U6F7y>@tpQ`JJ>ucz4&a3@i2->94);M@CO*oXO}{Oc1- zOu8)q05^`MY1steA%Mq+-3-7AfX)ZN3rre;!$G6|@vr3N%S+rh=08ud6i~Lpo@HC$ ztcuq8m?XwL8KDt5N{j@klazxPW+qAo3;etk!u~eJTRH;NY;Kub<+pnYkcgoZQ_93S zgNO`r2sZUhn*^A8hrreY_J_Tk))*;`a z1c31Tux5@ne=)~`4JQl8#NVGC7<2|6G)C2<@E`Ef(KR=?HOHXM|E~3I`)B3CR%VV6 zYRuRHj&JnSZpY(2PysOE#LD&jKrk|K7&Q}iF~ATcts=%#5Pk2J8$d|wPjEXuMU$)! zPC_Vv4vh74veC$5kcMIqlmd%P*F0ix0p+>w%?z{Om$P_{Ozv1E8_2r@Hx1zNGJvNPqzhT>9te ze_92jmrF=Kw&bI#I&n77XIA{o?F)6pp%EWS?;UDqSpjgxD7`C!2Q&=>1W{950B%&3 zgPuuHUQO1&xScg$1Ao`Z06k|eXFc(9k+?+w{SkzoMKuCwvvw>mN$eREMBYriGKd(M zh?6>8G17ss(_n5$QhDU>O&_$?U(uf#YPH9a#&@^k^xZOv5&Cw+iRZ{z1P<%e5&GM((4Dn+y^;VYLrJ>*AtOK4W=D*#; z3^cTyRGaMK18x2*&H?a#f$Xz@ZMvw;qCU?Jn56MvS{+BB#7RqHmfGg5sY)5UI#T;M z`6IQuB_?iOX(qvkS3gmT%xBt#vCO1#X?61X&(D@DShzj_GMKhgaP3!f28494eLuae z|A@08lxl(}U={ z2NzzA*WvJN9Vh-*e_QXr`|fyI_5)}Q@a}%ObenttGP8)3Hv;nHVSn>-zbCmMY~g<) zG5LHyevgdij+Tc%FfU(TMAgYr0XSrO5xeNp?3FgStqdPTI1(_TjvY7^2sDt&gHBqu zNuorBI;PoFT$GA#huGOWyLW`Aa1R_z0?5-Jz-^*%QWA}K^eS|Fb~A5c?7?hD^}N3` zwQnVmTVeig++eH$sD`^V^2?CtX$3GM6ZI?=M*;}B-5zzkYvkK4E6s`H37`nvjeP$5C*gu0Sbs` z@Ss1W5)!DYR3J8z#UL8?r0-SU?Xu0GJ|)Iu6E1gN%tK$_ox;aDL+~DqPf4^nV3HOr zzg@?xM&OHUlt)sMgvWupn*VSLJPz|8%sh~_S26zyH`@RZ%N+xKI>3Z9{~<9;^7e(Y zMcaco^^CQvZTl^yX*wHY#psu)hSVd#Mo9SyKqLvCG5{e7C=}zMH_yPv{Fi+YtStx` z{v^7c+Tb&XjNUq;v#~7R{}Lr*7JMZel@r~8sdqR%_xRj!f ze(K!UOaPNOLU}&jVq0PaEH-Dvjn=&WE9qmi0+i60C2l{OWV-KamG11{Vmn6dbISb% z(KhR{-d2u-1L+EkHBiyYa!ng1V3(L^0(Pf?csQKq%#^}4|?Q8eGnf=^h$n8 z2id9nfeZ>LpNqB#&=n9))6z5DpUo9L(J`>F{@=_1mGg_le=N(qgPrViEgNIXdn|xl z1q2)xSiEZ@RmM}b&-d=B24H4$z_qpK;rDR72lOiR>FSj>dVck{EgZ1N0tg>~pD5i1 z0MIiUPw(k5+y8SIU{&9@&4B!R4glo&)aGA_*|lF6!EnN(tNV9wv;BTD`lnPO`7uTS zHd?HtDAS)0$^%NqIuIlIq8~!wXr!#?c-{9GnS8CG$!$>DOY3{N^^OR^zqW4OD=kwq zfR6FP%v!DY6Hg5=Z)aTrVEzH>X`R;kTf6{ic(gBBfNdqKLdb9VhP>LQU z7d9}Q+!)F5EdvBVV&L)Zpqo2sjAHB4Heyqs8F28i-T2|?)i2k(v5w6dy`KY}#0uS4 zY4zX!j^2iW?|JqZPD#V!^B+#l{|p-17=g^}Q@!#S4u6DTvN;eCG-e#-q`XQSwVtH~ z2|{Py)M!fdtza1la1`H#tj&>~+J{Pg2P-57oh=U%!h-n5rUBqdQ7Rwc6#%k-0c+1H z-#Bm_fI5%(xe45He6N#SoB^-J_AmEkMZgtKyL-rvfK7;f0CUv<#8SYTI2OQL@D`k3 z=+?l${{w3P0RL$)0fZ00Pn2$h55P%=!Rmis0pN+h)mL8)aR8n`h)hhdE-!u3w`}KYZ^0k}0p@D>V6g^ZNOlT2EwA#JnD$u}>4XcQ8J!8d z!U;5-=p27>p2%|E5w#2;aCkZwE#iz2{dt(i9ICxRm z{t~ere+Nm<8STgQBKJiMGOfY}HtXtbI$5$WAia~vv>#(*dIWB_R>py+dLc`B)^`m!bd|^; zw|#H_ia1o>gmX+FAp@JZjz0fetpZexN7$(Zlcp{AALV*Da_l5#ng6*hM4ModGGlIa z#VjPaMaSJ<;yq{<2y}fX>Lx#_kD;3-6m5$4cF_aD=e3!``VEn~)ksoKiIUAs0u7j? zsV3#?dnmO!32Ldn5j$J&h$}_q*hdxyh1blm7#I$o!Sm&K?06^#1gZin@nUlpQqQXv zpznAG){)%l11b@==U;j#f|9MyjDE9xKFx^HUfVk!kfDQt3*x#<8hZ^`N z3c~i0qAcFA$*vLY$&Pt_w7#>Rl_ZYnvD!*YC32>pw3($C zf=3r<(b^QH3`%O-gSy}?Ep z^IMyp8oM@8`q$aI>%uXqs&nqv*GhOLrb&_TRvgr%aL8V929(z^AW-8be~n3{{Zg+@ zvL}E9I+s6?GWeNNGZzH}li#bEC(+3fmv5#77@2}LBWjGnsGafm^|zYvgN z^!6@9%xdt~_mMA{FRgt)#t%)f^5pAtDet20sP{CDjH z4d~4eOX=soKhx0-6Y1ORv00VAx6j(Hf6{x<<6waKxA6K|V2+^rVAqoIa|{BeX(vko zck`ZD1HgU&^Zt5b8^APQVpRZCf%bx~4v0B2@DpC;W`1tpeg9`{O93^p2H@+r-<3LY zG{DaOf087|09VOP09&^Icu;jWA^opjbvP4s=Fyy*4gh*Ug}?3yfTreO8qr>K zGXRlbX0dqcGd|9I;DvhH>OEnecdl-RM0a8|k{`$H9aQpNk0i$#8ggZ-<7Hs)DAn%g zX9m^!*k6#IOYiJ}hxZ30c$12tBysh`tbvORA~4kthc?h4)!TKTpE|nkGTm)+M@0=Gq#?f&)|IV#FH~^tAfpA+U8yT2X<^z-K-$pPp*OpCvDaf@L zv#Iw%c)UvTL~XlYGO1n^_hRiblYlaOG3oC(2$1SD#0ikZ98hFAV4;J7YxN(n_jn(4 zUtc>D`){uO)Cs2ELy#tq1JcQv1Pl)t6?bh<(q83(jzgbP>QkF*o%7xBSyy9J2gnpo ziHMNg6Y?w5Ty`+mByCtxKxgXEOcRacgL8djT1{J$R2>;d`pkv9LOm=unVywqk}eI*8i;o@f9#8twgyssWId0S{$o zK=`f}@{O7R*b(s6r*F|OUI+T(Yuy_7pJ7Pvc697Vr&}8d@Y7HG=4CI-*UbK(rTkIi zx8p$*aNu1WUHbRs8VD8%5KJpjKk#fU5M?F6SV?SzJxvDL*aw|SHnmxdDw%c-BzTLJ zS`B>>@%S-KY$F#df|MjD9G$E}b>^h8)jj`4E82shil){OTxU$Y$cZdQ4hm_fK>>uO z0JU$Fx+Bc=eH(~Y_()0JU|L9bqoCEDtPZ7aDPStw>V*KIeoj9f1K4I9*0=6o)Mzqx zuWv+)vPhMP6twZyt|KJ27=35ELTWwYJuH={fF=rRXN+~}MaQ{m1=_5sMKHr>guo_( z#b3kL1TI|Fp(^R~lG#PW|GH@u!1fhzpp#$}ZNuFdIhguq^#OZrRqg}C?6wyLh7%nb zejf_#i7E{v0h<*%Wl3|#zQ68f*MPc#JNs;|c}{+MjWTS{e~LyKW>*aoB@-1vuQ~Dg z?-Q3stsXsp{s-u2YJV@U85jr9V7xr_hrgzcR0loORQBj$iy=Omf}z$%uj3$v#AiZk zMMIi5{-GP3HyGdMY}aTT24t{`bOnIV88!=(Yzu6f0SS>^?^NmZeBS9An95xyksZ(A zU@-#D-2T3h-e={5!e zk{AGfI7)Ae60` zOiTby5!q7%F41i5BjeBnd1+E#4s8GySV_HB{-CWeRp($@*m!Ov)K*zK^-v*pTJ*2CL&rTy%@^ntA$_J`3qy7(~f|# z^2KfZ1`7BZhYp^dkqhC|2!Q%#&>%o0OJ>o&_ylAoHd9&CsMf~5({Sj2Ltr-&x1))0|K3i z{~8>pYk7`M0%uDVT920ql|hv=AYQe`8i3v9V|;S1<%AfH1N<a+j!0Q)mw8Pb4z!60uBAE1aE=n3m~3>{c?{s zYWjC2W~4|x+c#mf=XDNkN?^9jB?zae!IscvQb5vQOdgQXp?*s5!BmE~W9;0V{}gl1 zc~ACBF2@Nq^?ea_7_X)J!|ENdQH#^{1EqsboVqX5rOxaCvH1_KN~G?YOrbm3vgvW5 zk$o$LDC>-IAeWJ8i)*?=T5H`v4BEI+=R7iJKJjni9nRRYDNI~<@GI!c`(}&xKkG6= ziT0;pp@fFaTRFSqp8yQm49M%8q)lOzZ4T(T?0YjKY_y8d&>kB516B8ynR_>G$zkx? zXW|qV$a6Da?VS&)eH$9IZLMnAoCOW=Vubz1#F)GJuV5o7z?$@&I5DjHQW-eJ{O7|` z+VV(|a0~R#bH+V0Ist0=tqY-VH*~#2nHd`($y*sy24_+S*K{s4i*~L8$0Hi!0JsRL{_8kBb*Nm4|2kE4f z1aiPOBRWa)?LCY(2mDC~rH=PNcN`9J4#_u}rpfXD98>xGTOGS|BlDz{> zGXSZrDgUW<_rD06Qlc!S8>R|ED|cvNdk40<=#SYhjJ#4`hyI=6%#`1(Od%2FQ6khM zGKq3bURGOm(Z)PPI~tR0#Jzi72rUKZ2_y&qD!DNtJOTNxWDPJ$Khd~!ItwkK12K%-K*Z?P0SutgfCX`yED&e6`*gI*O z_I6EteSkHUE9Bntx<}xjVw+ygLM7YMK5>MV*gv`xrjj}U+Y7_X6?K0x<}cx~`Cjb$ z2X@SpQ5?A-x^Lvh&A%i!12ZEV=Z;fta%Q6t5Tc((Fqr2=>k%CCU}78%bhOj_d|$K< zhNf{yjL?c{*CCG86cADQHeNs-*z3o^s`3aMzz-6^v55|lq<RaD zyV-ycG$#BQu=NWCP{0!A3h%d-*}+qcXbMiP^(|Su5cIc;SKy{o#ir>-yu}6*C4#Ch zh!d}d0pRiC7X#iw7`D9_UD-2*U^Jpsp#CJ`6&D00rqW;G^fHCdO4Ddz$ z`@8!0*6XeH90`yE0r4t;k6|Ni!i^V8x6%i|5l`L-h!b~a<^QsUaySJ(OogcH5{{+- zjQ}WX|Lf;EP`v$kqGht`k@hb&n`iVjdQ+wzT!hwu zPE2#}3xvL{5ZBwO*(yn$0@d3}4PK(&m(?!IE89QtN6pIO}b00-DKW$pvu zD*sM&B?rLWc<;W0Po6fW+%Oy*YT!jm*t@~F;@(Lc)h^`~9V>s5##{>Fde|iub^m>c z@>ug1Qa}I4ZyHQrr!J;;L*iRqv`XdLI57WsZD_^;TOY?5ZW^g5`YzD~*q#7~Dpy`{I6$~$|3A34U_c51n0Y;d-Q{=q zz4@`R2H?x@{?zxwyYF^80_w}gKA>&-@SJojeE{CGlQ9ker`KKs`u5vl;v^FQCA^K8 zukGcMZ`7cY{|;UK54#yW$O8y*TR32<#eSa916|}dXCl_qw`5y`rUEmGQg`iJ+ah5= zrfnqvI04Nn;H0w~exq*C6QOk9nRlXtYpE78TZsu0ar&KkK`x8dW*tg(2~gyJ)@t04 z{8wR9-Op_5IV_xWEmOsASMtMSink8kNCNcDUI_8P_)R}FN^JSsiW#iRN~RG)8oV+}2ZRSkvQcjLj^@)a&aIwd zVrcPXGGq-;UuR0Jv2A|j8ofM6x2=-Uk6~)_t!qo@(b^Jv=+*J9^1(nM=6_MM0_MD( z2`nk9(C5F|{5=mfZI9dAJ5OQQn4VJctX1&-_3mr!)`H8p(<%%fs#Ub;1w#f_a!iy( zcm%HiAPcK4rsHW>`OZwdNsy}j53&m2m`nisnIss{G~cV%fy=ngu>hF;Kixl;YY*>h z%~%6)^~x`08Q{PEPV=%O;CnD6A7uuLo-ndY6*F6WYR@FGlhN_cSI-X zvki8G=xHLx-ALJf?GvRR*sG;dD~f3akdT{_!i+gWT>r##mOi*~(9IDEwOz zqf2G^w;fDq#&rDo{*C^Q&H3K}m^SAefKeJ~R=3RYbpGpaKTl{q3c>r^+hUHI6i$JS zDfd-t?+#`e<8hcx5f<2JlM*z{b!xRS&=l>?H3S8k-goUWpYz-EpL}+vx<`G5sM)%6VArdUO27vuzj^7RoTrW5n*TcJ))0MjHCl^GA5Qhd#Ahw3{< z?hLM>QzR#U%866C`FT4l`W(^V$d@yZ=7@AV)SN*FTc1bDs2`g!xsvF8;^q1|<3t=B z{%ECA^|~bq8Uv+)*>QEUrpI`aCWk1KpuDcm`9_)qx&1JxXvg}xWEMR?5>{|xVr_8C zi~bSWCVl!@l^%6k5rW<_g+0+H0vxY7=;=EzBsHai!^lwz){Rvi9>r@riA*}79mjPo zyu`a99RKVXW#2$nc}_Z87?m&Ve`EP+DbwW6g%F`R(Z3S`^y+VQf26JjgGQL%S_YVT!oKkypP}d5i zG`TUACGD7;k()b@kJQF6$@{Szr`dzw>d%G}=l1plaK(Xl9#CtaUyR zgoiA)Nn!RrBgio`ogSnjQXh1Vd0tj~=THgHO3mTU%SC8X2_&_B*Vnl1Dh(Y<_i`GO z?JUlnbO#;dfn3D^7D?^u&lq)b+i;YPszQx7CZ-DM`PeoL+dKRAv>J8wNWCfy*nNQH zOAKb3A-`UwD7n1`oo=gvw4-uPz6p9fU`vm3I|JEMs(or5Wk~T)007l>$ z&9brHx_S6U2y04++1aD1p4&@pnx$#y-`iiiIsYm5z0ZFP&(6FDMw3pxl>rJ8pU-oa zQk&BJr}PKe)ty>o%V4xlcBr5K&J2B6wO7TQ0!Y?J1nehJ11!>9|E8=lrM3A?=f1IP zpGOqW_)FWBaR_8Hnrh5-Q$1lc!K>xGPvq6u3aJo&a z0J{0diKYK+|K|_;M!G+S2EKO>#<}mzo(}eWEl{=r=FKHDBB%ZVliP=UY z;$v+{BOBU?)q7#6wh?J>F9tBsz>V%b4Wkw$za~v z3VLv#VtZh4_Q~yHiyjw%u5kTjV}C*M5ooH?$vKqwfjPE8x|W)^PA=)w>1;Ft1?8eW zSj?R{mgOHIUCUYt_f^?^e`8av@ALdm+Ny?5gE>P7+UR-nf25A%dFv2Iu%w{N8E*I` zrST4y$E%aS4aEpe%oY-plvE1zUy1D9klLDmyQlRzeP2RGMOO%Doe5*gCWa5gDDPBv zjQ~WSSlcO}md&T)!@!+xno+_4anY$!8GRKB<-W=qON@1u{uu=9tBqQppN#p-ra%j8 z0eA;(pnI|kpgwcPm3RpE?>*|iqL=`F`OA9rwyXqT69D**fd4CH?5W>Nr`xs+P?Pxp zF!LX`0_NYm6QJMQ;*p&H`q~bU2kdpQ^>{ggFN^a0JKiZxXx^;bR;oeDm9v>rkP~@o z&-?JQEan7bOMZHar=zi{_xidk#q8F``(>wYG)g~U!&k<4AsmPVb!9_GbO1VHLzvna z?arnF91BfFCQtb28Y{ayPbmz1`6dFcRPo(I0~67T;nw@c?S;_=^Bgl^07}rS2NIw_ z2TrLT&9Tb!XXfO73UP`({R*vsOL85+aJKF}tdf+kSwojJM>U|g2NmP_a#oSemr_*n z(H;K;;1*}^G&5iz2LY-(P;kKZe|LyQyIvK{lPn(4|E6gUEQJY(qA{fy+o=P_51hw) za0VN%ZGIufeDglb5_cNMBUq&9ajO6{bw3W{EhaB*%zr@K040#B+~$8bS+qdh0D6r} zhFKwLZY<;5|hXUD(E%w{{BllQ+1 zg$j^y(xrg%$Jk)5uQ=1?fIJ^KAn^5kiI?@avK>+r!1plkALE02uMn>S`0hy_r=PdO z6DEM40tD>f_M~VI`v6>Av>VIP*8OuBU=QAtz5kuL4@_P|G9EQNliu6O|42tygZXF` zZNMU~o^zdX{EI>a>r3jdY>&?Fmwr7+0kMCSz2-$xX_8@a5+nZVVSTFR4Z zmO5MoSPB-QeKL_rhSXOS$^8g~tK{W`6u`wL%XG^IZd4B-x6h~EcNxX1OX~E>mWAC2f@^!_Kw-S3F4iTylg5qh^EHiupeWsemyv{J_A^R1ki!t z9~`~se2kVK}R6&7_CY3KXqNg+)`fd*5K*{UCXVXD33R-CZ%^z z4tiwVinC#uBLb25xs>zm9|(?@GLYLlOYhu_?hL6OQJ58Zq!TLWm3L$7m!Q4vh8OIL zjiAjQ21Wrfn>G+Kle~6WBCvdz%JiB-(I|9CT1$0rz=Cf}r&w5-<1iQDRdb}gxv#$aEpTK(otrNba{h!lc z6aXZ~c`~sUfL8(dIfnuMuKt+`#>&yoW+%XD>d7E$0OpxP03Vz_BRc@+W|z(#ae&CT0@j`gH>qu&%hsykCEnS`~<{Txf21E+t^?}tmFiBZqQkkb_Q^!>W8_V39|4oXgjz$FXP0Y0K z`Sc3s-wOGpl-&iwoB!t*8eg#e&0lDY-Eb{m#}~N%ymxFC@Vsk3MZzC&|Ah$9zy3dt z`1t>qObjyZLV|ajf}&Z{v0=DBK6S#fWWcG7FT?oMoFK#KmVm%eHwFM|1S8`yB6RYS zz@p)?j;L`lcpbnz?JD~auUJ|tpkMq7=;}%9A6^CUKS7rX#*Vu1CamdtQE*Ua%2Q;*Ys9W%U3KSfHjBGmfku;~JpnE=WCby#B99bqTxxA6?dpPsj;$iM(V zzkCh?MA(A_F41i|*WIPtn1VE^^v|k)ztW3G$#cJ#M{=fC8bY51qd?U-rD2q8niyO3 zvPBbh8KA~HZ$LVRdoXwwn*;vo{;f#%zr8r01nDdq+?D;?!=9bN^l259506~gyQ8zYJ72lI!Z z)25;GHLPerpsJi@?+Y;psI8ynNAU?TJ^E1u%ALfV;^mX!SV*&r-{Gq(} zHU7uexaS=K$Mja-5D0Te(M$1z+qMcoI{&@*+8`J;02~Q0+x-~~WX2zwp(OR^KP~hq zA8St3Qixj$1k|sMpd^(;8|QEe`NxoNeE{a%2LYA?@$@*s)N!kF5dfZNYP?H- z*(9{GG$n24j{r^JQbyhsM6T=V^0mR!bZ6?tBaX6JOH6J2*o)22KwQ2oWH#u2@U}ZJ z2aXdJ0I&X=Ia}rX-Il?X@pH^MUJmF&C_4gPA5)&EAZ~zc0^rkcQeV9K=&f!L|C6l( z;LU*eubKeb{ZM`V3^f3jdX-*1XD2{50XXw#CqQTPUwzdy02~8=!GM3z(Intnfy4O5 z=ZL_8K&<(9!l4pBnohw?1z`FGTr%jP-l40TsE8w=&CmItYY(~^LQdYXt-pr^ zkT9*QzXQ#;H-%HMnZf+Hv+c+M=Ifq!^V3qlQBaOi5LqDW9>RA3WX8yKUCfHY^lh2z zY?2}e04zrjiv{8c$o_#Ebsjtq9_ruKm0h@O_m=au9Jjv*&zJr`+SUTROu8+R09Wa~ z6b)!LH41(O$Yzpp>))-^`vWKhI35qO3!u1eK<5ETwiPhH4d#G*vW7ED8jvZEjXcDj zfC)irQoK{1!J8kG({>u@G&-}BxiE!>A*~R&OSdyQqxT4DeqG58QBzy{2yIaTG+z)w zz@`=IOOG-ek+UnIJ@P>!yID+PhT#8n0vUnRxrHI*GlO6BOjP>kT?a95wE$FEt)l0rbh&!a=|q+$f_p-u-Td6$W>p`F1D&%v+4wfuMiq7TJ)3 zW@5j(kbEJ9n^cuf>nNf$-#R$yM@4DB*!&rBr%=F4sxzm+ElUoJv-#7ccUWm%sV!TyL17@lPD@^1?dw2jjE!=tCbA;d$F}YZZ+Dm<*j^a8 zDTu20KhMKkOatJ%0Z!LX@t$b_I2h3W{M#t2_W}4#=LGn{`i1ba>VK&R`~_21e_O)$ z-`6+*@4joydm|7Kn*jLuVcH6qmjGt(vkFJE$ z(Bx|PklU7=00))AL2Hnt_@$f$dngG`A>SumUs#Zgb4`zDCFAOl!UoIOdH+~#i~gzep!odc~paeuNpPS{Cg z+G6OLvp0p-4OkPkw4#QxRv0?6zCBUyd;oq9iJ&)K*?eeDosI<>Z6wzm0J!!w3Tx$b z9{zcZbWU4MShD~y5%@Azh51SskUSJ1;K*_#km>-$Y^^+IXVowbz}cc30+r)^0^7^} z>E&ZQT)i3<(d~fVhOP8*KCy}OvJQZp{^BNpQ_eY`+V)3T1MuaS`Wq&I?-dL%>f^vc zj{dGdj}ILnIEV>g7Nod~54+&fX$U#wquX?NpvVU=78jz)`JKw2e2@3=Q^M?=A}av~ z>BUKYD9!?v7{%t4hhWZn$}%xCI*dHS*nsT8zEXEKo2lNm zUpFyO&$QX|Kb-zR)tj0>y^$wCDLL$~zy}@?nD5>ie9E^ zlC%Z(ey7PgaZK#@<*XcnrFh6}kN!szMmx*{9{WGJ0KoBh0+3b{z=%`q13;IRRCRLAAl_Vb9(blT?LSr0cIz_oYvQPE8zGvc5i1Z0c>|O z3lwBd0MR+*>!vACXje7{(Q@=iv7jr6Q#Lwz6_dk~#|P+^0Culvu`6Se+@)Kc!Z(!$ z_7@>YpqETt;wkAqw8?D}QJ>ZlfR2+qK1w@u>3s3UdaTMfPiJ}918MOlzhEIxLHq#3 zcc2t&SYq%zZ#)KoI2l@{VPyNGgC6wFA=l}`3IIcR{;xF$V==jFa@lwy+g-W^N#kE7 z4QaoIKaUymYt2E_Z^^dAhOK+_;QXIFwm@_E>+2E2N;CjCwV0Rk?Qrw_UYLDK~O zj8_403ew0v0G!+hfI&cZ`l}t~#PD>3uk`+&!i=E5^|HlmkaJ!Nm^T1oNoJm^2|yz~ zM6i^q5jDB&{)W!@$?c{YJ(v{RFtKn-x-P%NJ;)aB9b~4b* zku@j51tQejR;}hF*ojn=6V0NSq*dE1z2(a?PGW-fwDPdHNmD?bP5%TK#-<%erQfC7 znQT}?8#S_erS#*NoDNbvm9;^ssCJaJ0?ynG4Gs&sjPOc6rvFO>fJ0^oJQvVb2k`Pg zK@kJ<-_1K&b#^eHv&l37`(4Xd_I!LFmh%U6aZdD#UH{7B0KEFN(7o?Ik9KDtfX)Pv z($7)@u;m%dJ^*84i9g~1q~p93Fl|HyR1?5;Tw`?%hXFp?DgaKhq{H?A<}zf*C%NGa z-$b2KI=*aDxIG5AL4@2rHRu^NH7+O^FsB!Qa8&VfIwz^L9IRcqqh_}{<%p3(l*jO! zYir{fPgl**vwSw<3^X*OO z(s}PK3J|8xOa}>?vmj;k05L|SP#bebaWh~w9H38TZ9_Ynrfy87l`pbgygbe9;+QfW z^ki>_0M`Hg03dz`qS==8-95l>?^oXQ_Sy&Fi+2Z_Pr24VLk+-&{U2Qe;3-D} zWCs0}XRIxL@St_U<%StRH2`PreY*qA6`_s!mi*OYRcD2sG60GSA5%Cx#fWnOy#+Z* zfW~Z<6mBi(1ih8hnjKP|g$Q2V%tR9LE)2kJPk9MoXz4<-Njs4>2c~g8kW0SUM1SAF zsu8&!7^(T{{f{5z=}| z<7mpl!BV#0H$9;}?*xcT)xLK=aQa^>kGJO(rlfW=DCBVIBaK)dGPN^&C6B z8gy^5V|yja&KPpSc$lhYm^A=Zg1+hCt@1P#5E?=3P$njcz6?Q504(T)$}JkElAQMWm{#q8d9vo^DXuLT z;|t`nwdA{WYm-Z*G@6B=Th);?02s^s+;n8tP25`H+Lehzj2%kV08{*1IAC{bT2!2o z06q#1CTfwdQ>37>LZfkRez6S`n>tjt7Uq{v>+w67W(^E=zA(96gO-rCw1&`0cCFo3 zUV7KvoVo2u#%n3&)aRP|UIuc9YuXMaU*)#OR}_FZt3h@OG`&EB(JsX4Jt`hqmVM4_ z1Aro1{w`uFj<+Taz%DKS+p)IVB!B-cwhDk10Bi#Ayx&+TKz0J;NPzbq0KWD5dg6-- z;3I%+SpG5TR%ifd2y(*908c$L*=9iBf8Xc1N4=SllWPEc86fTWe}`-OyEtgzbsL+@ z2ziIJ@NRB(&#G9vFfwf;=$SevRWNwe3+M(RM8qoEk_g9g?$WJJJ~W_7?vC;(XVGgK zBLI>|H2N6o3T>fqw=922#V+KJR5dRvSw!-W2!8?$iV@2c+$YQhjj4P71xlAC(i9GZ zt^t4+D!55v)+Xpt5a0$ehsFfjd|T0sP^_(jaRD?lfL+^w(|751BnyjfWBv2X zWod)&Tzml71kjAwZUT4{egQ9(exjj#+x7xjrMwd0tFJWnKmR>^*yq3OBL8hCJRVTD zYuM4T{%3C`+Xc{ofMs5iHGq@GI)AS8QCy-SQ(g8_3}h&a&C=Q4-}3bVVA>!Bz`LL( zw>oW%4wvqsd4&v`0icwbvRSAr%_X0tv@gve5bU`C5ORSm4uG#=&anX4v%g9Fw8)vo z^`9r~t2lw14#GjUn#tHbPt|$3hVAx>uwuV8{_@a92Tg51Q3{k)Lpvu ziN96b?R0Z^D0S|FQm@@4>kQWHR&r@a7Rt3!T7x=jF}YmRRR?1`oRH{MVO zz|Ez9&r(5l?{LuH2*yyq4m$zz(d?RlMH~U!y8`VAfusU$TSzC`lx&p%;YvzrBa0;I z({`voG;_cz2Ar3bPMXxy&hHSw?M>M1l#(bhs1&9a#w~L#ut~m-vB8h=Qy(u5U;9MO z2cWH^X6EnPn+o49ZH2y?`90EKj^k(R`ir+nulf#x{%o;-MdB(N^P*T#A(Qu&Ui@7m##p03SqnX+HS^HNp^w!-i5z^zT=NbdK{OwxTw{z&Vm{a$xtjIpwMs-5>w ziYY>30{Oe`zB!H8Aw3OR=Oc#!o(_08=t=BKz^P>0rN7zciz$S z|M44;C2^c$FMto%)c3gD)cw*zK|?`!{E13=Wr1GsljuU)y_rY73!FE7YYly^|{ zCaVCBY0$_A?K;(60VZ%y(9(G?)8Lo_<`$48zNB-^Ac2wuI6{M#KMIZfKKSX5`QQGu zC4Hj#u9K^XRPPhSNgqVNX5mW{;G?7s@;vF4koOSq(nVA(n6oS5?y#cGkvlJn<7=uO8;AI)zBx)EYADmKO#^&)=~Dg{1s z*k1>;;DV(gVv80l6Uo<>Cw?@_{=O|R`v6=Wo}romct60$5(=0t0I!%+JNp3q*&G1> zb!#htkF2JYyW?lL3Sdal0Coa&4FH<}oYBk2@FjrX_wfNd6fk*g2!=$a0btdC+b)<_ z0qoTCeHwe zrk_C?ShG8`b>EJH^|xq!6cf9hH~y%gcJP<+qqbNe**lHH$|?X$vKde*vg3tF{OYh5 z8nwm7I`1A>$G<+O8wFUorD+U6L4%?hLjbZl3MfS?tFA~Ekk%Wxv5r4;_r&-^1LuEv zrJ?yxz)*Z!KMr^4wx$LS!5lZ*5?S*BXxS3&^)SHLtuW~W2VwY4ONL%>qpiBM;a*A; z23Z}4{(?9Tz?1qPF9YO2Ku^vAzpH<|8=&k3 z@VRaV^sB$EH9Qpv2;oO#+FPdq@MVCm0pPT?``@ES`kS=?obCavocU!7fI3q@q#-Bq0)W~8f_}WMCSjQ9>fs{${88KQw+$c>`e*%{T|nL7 zQ)~io^}m~>Jw34B`}eAD`l{yfKtNRkaQVvH^2{Ip1Mr{HGZ6^rqo4{uD&4k7fZ&?} z`8k^an8|v&1U?NhE%g^u4Z9Mhg9FJ7OkJzZl~(Aq51$ z)Y?z#?T6dycumC}8bdSC&l_t0S`(9#BSfr(cHRhkfPi-GZWi6<)V}q$yIwDWVYiTm zAjOby=T``=LPnrI#}FD1bsH08?ExF!TmvvA`aP#r0C{iVYyz0eK8`&I(07QXEeF)Se;-9w<6%j6JT56(Q5%%s$qT5Y(~(mS%frZ3A&|Bqcjt!(mnVS z+DzIh$(259LZp3pcj-1KJb6s*Vm3HJaK9YzHixrsnrB*nWMbHSLJ2 zT+omMs|pd>Zke`mR%{C7b7ARXVn}UW1F&-wK-vt*0s&pRg!&Y^KtQ|-K(qj$+W~VN zfG9YH0$!!S{69k<0K7pm$R+?ztN|FG?;-(S>V59t{6SR-aOfXY*G%2jS>rp%pTt2I zM}UP|2sE{W+IFZhezLR@PA6;Me3yh`Y08Eg-#=jxI7nzVzYj8T!#iSj%TwZ03hFWg z3Py$}CoYY|inIvjXCdwT$^nf6XnPI(|JnPuWy#GOT@(e#9HWkwe9~0N(GlG-qq{Hc zOFzK$#eM}pkG7u2udpvXA3&G(MZ04~&)6OI@{(m+a#d;6$Rx3dGyb2)M-JOM z0dzll$S$Sn1waLo49^_GYtavz#-k?>+7nOZJIdd|UfIQOxREMeyT<=zy}^_9L>v(S z;|gfY1ZSoY9?T#xKP-*xL5Y9nvMYv87o#EOZbT1qBxK)0`Ee#&P?u7*5~RYTU74+1T$J@s^=MtG&QFLb++k3oB2m=p{f}RnF~d`ypzYUO8xoKBLka&~t9mBSC6aeclbL`(~d!4N>dz-rq=Fyv5A4jTX zfwR^K>TYSUs(}Fw8^a*~ne!~ED13V+!u=}B*)L8(w;&%egc2&#l)pJs!mHO0x%#H6 zKeYeOrQ|!s)YtccaK|2OMQC_d5+Ud|z_2U5k(Ak#wl^mcLLa+ElNGxeRMX--EP7zU zjxXjIGHN_lC4b?^S!y#V~& zI|cDJvP&YMPruW8`tJFA8Q=gV*+&d zQAy1KA>!uP!{Rd993G9$$mc%*>fNL6vt|E_Xlvhk1cmmuDq+{5SULw9ZE#c#Sjpf} z_8n;j;W;%J7{R(!w)|vsSpqXFiGGQS)ODe;vf{H@+a4Z(L-s3Z#As^i`F(6bP+GA) zrrn6C;?wRe>&iaok^o3KM14DZRl@`FU-Z5JoYW$^XOwopq?|N)OvVQ=%{PmlIwOMs z_eB9|65#Sg89xnI0caUu4$kx4596S`8PL~N0a*3>TMh8N@c>Zu{#m#RU@{(MuLAJz zwhRH68@rKgfbm^-T1ImY7F6M)34uxjEaPDqM;fQ-1{fX>XE%>hXH~Masf?K#Q<}4U zg-R&ESEHH2OA)~&3~Flr6D=_0!3+d~$R~h9_A74O0j0-YeRo`~ZF3*>`5(HKde2bu zn=Gj`_k~xQ0%1w`x7NN`X@F%YkFuPsaS%0o$*{kANb*<=q4{Tv`MX5rz+C5JeQ$=1 zKO*(~6S;?eKJNWKstVq|+q|IU-^>Skwrh#Z{i>4-d#1TfF*M2?s0w?gFer6}91)_s z5t@?I8Y2Trx2*8YhNgJrh+sHCmWi+pZx;WM_v?MUJK^O#e&I6k) zBYV1(3aY_y$j*hJ;pUCwI+}o2IW~;J@8a#bN?JZ5cJgm5Nqqf zQvG$Jf?8ua@x0n4}#rj}z?&;5DAC`z2mFyT>zEx5ek5 zutsH>dtwltkA6S55P$r>?E|qHoY=WQov%c1pYnOgf1ea34~;qpTNIEbHrv#jtBSWI z&Xwhc7Sw$vA{A96=5f#H)MgR_uvq^xurZSMxl5qM`kNF$p+nV{!3JNM!X3zog0#eN zZca{!v%B$*mJGPXZGo_c1nPs`ENgox{be6>M~!>h5fHHpLK1g z3GM=FnP3~s!$7RLA#PN365F5=SqX|o3H5s~3`PK0Q}8(Fpt2_bUY7N^2LS7S&O76l zz>gkj4#15-|E{344B)HV7YJW|DepN4;JqaTz&QY~-}_LGg_!_*p=i6n==B02cp; zSiemMl=XAMQ(Dy7ydISKbf8kaC&qv_`l+dSQpBoq&Dgb@l>}pVZDLR|#cx?76Hu1I7U-65IQQLeARJ9+~ zWj;qYV#&1N-vwOI?vpepc8K5hp&dK`tRpUpbP2CE`-hkO9p?ey6acOObQFN=05}9N zoU{snm%O?~{4;C|GXe5zQwI3Sde-=R$N^x%pKUDM3ITXK;A+?jyk9H;in=Q<15^ZH zBuoCA^ln|28!4?F?Q%}p@f@OksKs^cybnW_gEgVv|} zoFvO?M2#{zWOsrk2M(d-_J@t}5Z?Z#mx?iz3xtVjR0M&F>Rtk`iC2ukl4o@SQ${}t z-_tNq-`IzD(GUbxnamt;L@dGzmQP^Mi?RcoC1Ru9_YHo)##i8ofG2)YN2Wz#9zaR- zDxO?*=7wo?a9yM(}Uy^cp_uTlqh=DT43 zW!z`})5JgDgF5|)H58i(+W_%^6%-Dr4$f9E#=RhF_F?L%e+MH45=C;-5J0Z6=$wjJ zFV5-xd3oLh0np`N-RjYPxBm2pAIi&Tf4VYYKWXNVcapz30?OIIv>*ZMJe+9J^b!u3?04Cq5SU| zJMm971jJG1*#9(#6rF`zR8JRx@9q*yr?ixGC|wduNOwzjOA68~4NCs#kWi8CR$}Q+ zk?uyioBjCygZs>LXXeZ~?|Dz;D+HW!WTrL`};MunP>Xm}OOAEA=D)?cMpXl@9p+RprZ)nu7x32xF1#Xbqz2lo2MYJp%&!^DZ$XLgf7|*(X`=$CZuKBH4DsR4?jY%&I~xRR<9fPy@aWskR6qz19|A~M;5GUGzssdc@YR?SWGkAO(3tSJ6+T zn}RMuORd9R2)!(jhR+d=1>tIHx%H04-AlFuaU!w9z(eMLL9H`|hUp`Yjf?Br_Jex&_1NnB*^$4){@H>{N+yv{ZeZ}(e<@6$Vb?P*W0!j{<=VUyinD@staFrQ3L4T)N#OLWV)MJQF|ABgi>7Famjk z_p6K^P(~p(0(u~SHbsEM@0Cp}>44I7T+&B&cRQ{8@)sE!>v$`_N^>5_C=T7LViK;<5RCo&{`#k$mG|4sd+ zLW>+Zyx68A?cmz$Uj6Amb+^p`K5M)B+y9 z4AdfiLuOkDbtpyLsM-sn)t1#+n# zdKIq8{8_KUjR2Y!jUEL5E3)y&*;RmZ1Q|Vzjs?k=?L3`J@T2F^17#1C0tgp#q2qAq z?!V$GeBc$RY&XriHVw`9ESDK_=T&l(HWvOR;}NOR1pK=Y;epVCy-Si>DMU(e_3`r< z5)}lqoG9~S`z?km^}Jvzale@IIpUg~+G`%8er>5@gubokozvEy<}J{TQNCbkpYu6= z%hy3@HkcD5is)DJmp2T=;v&F5W*mA-zKy3Tnb;j`!dFb^3175z#&N=8&D(~!7J-U4 z*UWrItN2hgwGo2`7ly1rX#{q8C5^tutM|BZq#t*9@eZ?t4kh1O1x>_(@}P8XbQ3 zCUY4~ExS9XR@Chc;G9BMNX-`wu2BKdH;=;5dtLE;qt?}kr5VZHsw5(9T4$1KDc_r| zzh&BwcSV3VnMI~bCiugBqdaGlsrN~KlK|O4H-1&7*FSv-{ZTY(IpLyKzE9;s?uME) zFn1%f!kvBWMD|Ryr^@$hN`wDn8mF@bWpv^f#G|N)a!5e}U-Bqxjurx@qc`7)DMhRe zrzHf&B}_DKqEPyhY7He}4!Lmej{jt(zD_HJ#`{Oj3b8uY7deg=kCt1T3%nG$qv89` zf?unVWH^VBQfQ|A&-=%V3^Znt$>>*Kjh>QX7_ahw}HGD2#zalKC%pUg+OD+elZK&y6WlKkMh!jx}lIkZBv zw-|0!k~o7IlFkQQ;vi?wxecGN=lyN&ncaz2U^keNkBfJh~3dF`<;EA3Z#jywBu7)psN)T4E#_N_d>I1ls{v!C5mb#|(3{GY>w`2^%(AOL%j+>Yuu*wq@uT%oMrBCzD(} zzKu&(_0ql`E=BR=>hVgYN!YESYzfr_W_Uc7tRoQ^LDMxelqy0yKVeSjqHO!moBtJ% zh~ixtxkU%OhCHlq!BwDnj;JS}-l0rIXO9-%l?x{qLc49svVKOJGtpUgMrn$s$m0#%dyPMoQRhw`9BGi82pq84J0MdQw$-XP z)n7vNiIj|_^*A~xkupJl7cHwQ2Zb!zR2}a1qG(5o{JvBCb}Wg0i1mk|o%#tK-n)5F z$91C814mFn5XnC+>GF*qE1Gi!&en&*F`d?8|N76h4-EMN$v4Kw4{c6K2HbnuIB6@p zREva*Cb6N(m^Ys9BctKzn6nZ=p7H(G&m|c%`n>Dh0GNdT`?!;t_K2Xzr58H!+Zk^e=2Q~kvBK2OaJ z$g#wENxzp?eLF?*Wnsr@w&>W+lk*@9>UZ`F1fWBBCW3q!Te+lzA zzJE=08Ogrm6Gk{JoeQZ*3#FL)+UF%ert5N<7kP0~{u;mDNS7VrKz~#gXppw(zsBJ{|O3Dw^i(e~AQ$ zK{wdQ6KlT*pX=6QMuvlUm53f*s>iq+*RJsk7_$)-fh~%*yZ5h2rMhw5e=ER z!?!W!oKK)2QS8Kf7Qwl`=tv0SGB*y#({w%OE?1r*|ji1w*cL)lh4bDjO9{m8IUH#czb$PWv5r0^&Fdb)gM?Y1A zwWlDxeBYBuiJ1bsp7bIi0JG;u8kgFKLQVGE5`O)Lw6rtDt!=;QC~GWT=205N8Q&iM z=MvJ)el2ST%@Vy6)(RJ%cfQyLsf#$yE z1VAA&`%7|6`}+s@Dixt4E>UmNevMvahj0ul)lGnMPJdBiJ}s!s}U8g0(+t-80Z_oIo? zXSjdp6u>ER6z;ReZ7%_<>#uGiFv`MD?~ckr^=N33Bdk3<;K=Sva#Ve9N*0t=E`WWNUO(GXM` zV!rv?JW>FCDL*AHWBqa9TsU%#tbtzY1GZphz9>Hf$50n%K>-p0*9j^(oWJiGsONj5u+BD{{3x`ab2yFp zy~0l-AG0&QZT`JQW+?{|;ay~1ODM#Hr15&+6X6p4%lkPQ*Z5UPGU=uhQK1m`mB8|c zWCgS;Q#9N%Z-TTzZT4nQ+KWx&P;*%DpAmx`q9{Nr8CEn$473Ced8JJMI6`0q6Eo0O z$|45#k?)&_?zw+i<}g{J89lvLk6Dcozyjl+WwM3{qOD?;gI(EHYcHFc{oc$xzG#tr zio*v;E}Y@@*a<0OdLSW!@%!UXlpm~Y&K#Lc7dL78><434FJGe}l@|=iABSSutjb)O0Pnrr$63tn>r4kBt7|DUA_d1@;m<_ZLJ_fQ;?`nS zT#{t5nVXZo55i~Cr`DG+HV@r99*KW4@8)sSk=J1ex-;9N0d)1_T;9jTt*Ab;Q9CI26Sn!(oe}={>~k!AxSziDhcdQYJ8~$Qq){iPuz^U|3i;7d+Nu83-tu zm|?hy&WrTopxS5G8>+PWDigz*{2(GhOXNN+`#KGovO=eOK{fXaO2I{l)Zu}ZY)V`k z;IZrmv7&khJPiiQWdK4L30wEoy^$wP+kp2QI1<%SLHo2E?b;+A*xGm#NO#hnEpLjp zD+*v=UIzdst%KKw!hqQbpnrOfvz~#t&5XLw%bx18-Fma!z75=LMuWW@-T|{;<7jlk!^1MCYD0AR) z$KXx$hf4cdvNVPE-Xo5xC9l_LGi)+)ebTjt15N(HY?ui-=z1qQGmFTT^{6Tb<#wOWm@(&tIo%JE%Xzc933oVfefAyYDyX z7vOKywW0}#Rv%Di6BaO4z#OfpYIr;T&i;)3l;4u+&Cj0?7l>2gwmA@_O)dhV?9U1h zWg9>EXXAx#HQ_HHLVE=`_XbMslj+P7ygDf;5*WZ+5LZ0bKxMJ4r&w+Ve^NA*ffgrt((X%OmNYzHo5f zVo3w^L4i|gLM{?JuYyn@_Z_)0RvnE>H5s`{8GH+a{MEpN4V7L>oiFy4|9it#pEgEj zU>zS}A?g~QwFxXPy?QPt@PeiTTwlO9frnS0AP=8zkF;2Xfn4vXvQ}A_SAH;{q>uhN zoeq}Y*!2*d;nSAI&%LUzAafR{fqQ- zKs59N0X6&@{v2fGuIah9zN~6%d2D*h+5tF7TMaaEz^jaKi93xeu6~r~)|!kL$jua@;rmd=zdrOd50v%IstJ{HfJN zo*b2!Xa`7hz5IsH5xQ%0pU+_8CtDfMbH=f-B)~Ww9bO7NoopSSguY4h41~;V<-Y<@ zDQu%DyP?1iFr|yghOBlfz^QOg2w~Pv;Fj9!z(jV)pYZ3Z;3A>EA4pj>U{D=l?Y;>& zgeb`}3#!S*Bhw(*meo>PuEtd^n8{7Seu8=_lD8$uXiGLfpypRRhUP4apT^`vmLBw0?|IitKpqbCx$DJ9ueH)But`ar; z+A}l1lJh1n_bkI9(7GXYmZKNikGpO?u?x;q>7;RFH?7IW=R-k^z)`?WBgV_G%>gyA z{~lJmQ{anq%$uXucq>!nO{gIDMmsvns!Asmu{CJ|&%tcEeh`d?TlxG4aue-X2A;pv zfE~z=Sx%G&{5WOVYk$V{jj^-c$qEU6mk`h{m87dd_V^z)U~2gY+U$dmy1>{ z-I}RhnIR?rLz!dGZGMGvDGXH&jl0FW*K)4C{yR<{^R%>&ekPLGGjA|e#$B8LFi+=4 zoV9=RVTT}5f>b{tTU=XPjQjfuqf=J;$)12OJIw<~v`r#EgdDN(1%rb22Y zfom5O(QGfb(oeV&=wB)W(?RkQs2d+5{Uyfue}MvxdT-ti=9BFWZ4oou z_rA|_aQ8>Va{oeM_Ly~LW4hr>unOc3K3xCf_edOB{bw>`fFYd5rv;OsAS1K-mEOZk zL(|xm@5hm5uh4zPs`hZ+k3=45v)IrbkO;1uRJU9YxUk5Y%o+SiYd{nku{RuAWZ^4# zHyCwh@Cvl^Y^qKT#Kj;N+c%-EdVG!vjD8uUt7fTXb^>HA6=n#Pv4S^9UReZM-|CBh z-S9YKgYpj%*i^zr=BRM-xoVUD;SgTpX6qs+Q87X+U$IOSsM482#RlrKF=6 z0)~4vcgee^hUY=&QwK>NAU-JmCQl-Si}%yW$I$cjQ^65<2pNmgj1c%_=S|&9 z{769PIi*AhzBy`U}yvP>6%=uhJC()JaptZaX_gOi$R0aD8r0^E0e>5_Ye0?Hf?q zIUq46fRL?vum^bd)nK1m!?LT=?FFs1yvpsp*4BpX1+tlUk{@6LlGmdUS_`lB~SrG72und6Q!GXV!73G+lgutTTEv+@q5V@r6g4BmXqUDPX= zqlbJcN8x|s*BfGlN3&nmIe?jqwhmvShA18lVo+Jm^04C0MK_saCD=&_l^F1OIE^!# z55Y6CP+-Hh3b{Xd0=xIV-=xIwBmS|bOu2G1GpM;vT0239aoAjSbI;14u-z;}CGfLS z4e&t{FVnWK7~Qq6+8%;AX4W{Z-_XHZ*2d|96lhcdJz%kG$ocLA+9n;m=L9ovv!xp( zg!jxzP43A|V%yb3ZND=FsU;DJ$=@b1f!!_sV6XW!MQZHWkT>mPyhdaGC$g76E3np# zzxlE(Iw+1fow4lrfj}4@{D}FJ9fX6BR*9MDja?bKSP%HGQ~%}fNKvNl<)Jwqo`)T~ zQK#+6dGzG7kGhpKr6jqmnc$Imq9wtcEE~z%a>sjJw$*JG8)>l9$#%7rD5C+03%H`a=n7&XcWdno4h8tIy!uXTaK3~%`>9!rx* zAqhCf1GXBnFwWQl0bjd;pH&%@*e`Mr5Q*^`a_-=#ta*RNV+Z!vm~@XhMj zLNwTbkI^lJ?ZdkF#W;UHrrj0fg!nx&qV26T&1^#@!+oojO&kl37xXvMW^)B2^QQ^L zt%5AvlmZ;Ttfw|BEk}IT%olW=`xT%?%PWczqL4e-Hu;T&vGn!{JoiZ z%V)+tQ{1`wXMa8~=)>-f{)CR=sPgiT*Xsi(x$6g&f%_FB>S01J81K|pEzUm;Jp*Mp zMj9f}GWYXxR4>dn!Pd#`fq6nrkOW+na{PSHIl&c8@4?iRiX%&aiwS4`zny)zyUkC& z{>ZBZwaX@P`w_>xx5eOuBr~?>a4{RR3DfUU6V*%^eqJ_(c%uHiex0>hDJUptgDr%r z&LiYOmhrk({`-56 z$OF}W$dsWu6?CcWPG|k9V|QkneLrH$#BYzPTng0l*(nZEow{)Ni)pi!5l_unXX^fq-~GMmOV+o$wjrM<8X4Sz&`ka)5qsM36v07951 z|4ycI8N_svncK*MpXouLs$j9nGwqEg~+@9!M) zLoB>i2NX3$1>l1GZ}J0o>XFpoq8Grg_J>9BL)%qFpmn0~F0|yP+3e3hk*!ZKI@L$9 zw!_*q^*I^N2Syd;61TVbm@)wFenrbwl=L)lW@*$bz{S*Ft)gG&&u4{N=?5eyT~+WdLsH zm_gbX5>Y$zpe5;5Gb`;1|Lz(=E2&2CV`}O6*gvzY|0>_7NK4{=&JPYoGYiwi&GM<# z4DAr8ny>>biYY+}rB__j@TFk#pzBIYK2AbtO4Ln>7*`%)HuS zi{m{Fr zd!|~)e-#hpKU}|#Q{SMZR@T#P1r>?>hbdbe$M;K|-&$n0;hp`RZvs;DVw;!zKn+@{ zI3vON=`(Kn7QBoazuEieSD7^ol1(hJJ^!v%IG?~mPL-ybL;|s8|Jn#?uh8&2!eoDBkFBJ|9+1aG{pE3nvnIwqc&#yuCQ!o}fWC zkwHw|ULbS{^9%q@w42jL+kvCk=y{~i1Qo$~DSJSmrzK+MoB-LG z7zh}W!B{Qa*VGFwi}z)@O8$aPLo zL7d@1_OZP7xN=UdXWD_yMz=ji_GKBGT%LvzBNN2$-_6t8WW-gG@^iL3T_wng4lw0J zx=|ynWMGBA+gEcBe`hgXj3~p;)@b&1B~pS4uuqbBC|fq7mbIWTl2ZwOlvOCut~=YV z3DQ$ETZz;4UUBa!XI1Y{4xkcF{azik_pveHzsBBN(!!`EBX->DiF;lYI$yuciW?gU&epw;OQHalZGpu z4Cy^aeNpx1C0~~+_G*PuLRlJ`pIE{|qK!(Hu+g?L`+9^blml1S!(J3+{WG;-tf?@g z9IJ;lLHA4ML-<(L&apT#=uFViBk*iTyoi@gzCZX8v^5MZis@viaL* zbthaW_&Orv3munYkGUZZyo+26WmOa?y!G<`cFbowf6uuLUYPO$}utx2RwL&)aV$LOlBN zgodFxj9q{Mo%Qw)W(yaDq~u$&p#h+vNQ5kk54+{SNk%V)&+uBGGR=rvV4|l9lx*jG z@NsJ7qmr#9K+J@wY$A5`86^YV3}G4X1;CGkObOsn(1WE%^naMM6*B4ac+KSa?WsQ+ z&_GSCe`m&0(+76+ir~5Ac!*NH!?V{I36xkG#&Lh%ncDs>H(IBoxs4m@CsVIH;9-pm zwH9k!7W_q2<7U#zKEvR~=;9;1Jo4v@eXTSxuBF*n2#j*sA>hMwU{@23H z(G|PNcBtm3y7Vjgm%om6Nq$al=-xS4%a!$MEJ5NsfFtfUy@-T@u&=#Mu-CKx_&)gH z0$9)CH7@X6TB&0;}3r55O3SgtvsxN$Bg*(3g6geabgXY!!46y2jeS|zH z1zrn3fJ9Lc+6d&0obmNapyAc6+XVF3Zu*E3MIGl(wjmSXeo@5?68HJGfb;1HwQj$3 zCkU4;2`40R>wO;n)HlF6qj zDnZ<+Qe)qwtgEP>1A*&drZ+TMbR+kjE5}JDm;Wd~LWkP_vcN~iK~lfzM+&rEbbmRz zIVY1c4g^>j1`GU!o_r=>pz!%`m>$fDYPBU{I7)kq_e$Nis-Y)QMqEPgQ@c{$N9ng_ z^G^!ngkAH7`O{=hnG=x*@3#L_D}aO)g*BHkRHbSba(_?=d|bjIR-XBjA0l$qNn3Qz zV&E;A&wH=3u0F%k`}(`pgAnca9_B+-+7b&*-S-u!W}%3zu>qfvYn1pJ%x9~dD`DRA z%Ei47y1IK5`tOZNAjwYW67Wp^7E1iZhvxyg#H}ZBmcD8V@S88Dz%yD9LrifQfxdc;WmESm8k=uOk>ET)DdlZ$=c}f_Xy!q%XHKf;8~~P7|C)6cT#cei`vE ztj(9?%5bqga*eFE4@AxM7-9$e8-Iw!oD-k4dt9yfB#7-@9pySGn+n-PiXQNAdFmVI zF+3+1!h4;5q|!oTpe(~0hjW%&fypNUY1bCitw`o__4Q`W)OJugHXL>bnzavqNs#0J zRzpYs$8S~r^he$8%GL~CJ@CH!Z?6|(@~^B8DXD6txx3MWGcosXMoqB3ZN|MNuG5JH z$3HII>*u164@N5%j-1QAl=7Q`h;beC>ONfXL#PAG0ARcmECL zED}8v2B09ca>yzVLLO`sM)3bo^RCzn5+e$Cv1d7E#lR1I@~{Rwkvt&_@DSU3Jm4xa z%(Is}z{v+=;y^pcz*Y?{{rdZqOxq5rPvckrj#lHKjfEy9l{)&RskZC_qk=8Sm%kj8U`$P^(ClhNc>N8Dx>O60Bu0ID;vyi&>K2y`rWX3bP zLLb;12DII`1zvBJrecf}z#}krH(HpEMT$Q4K&ohg2`of0{0$8#V3rP8#%!_$TNS?q zv_@-|qr=q9G%lI7t_CrSUVdd7-Tg85MIwc@CxEm7rqa_gIRB5?a=v1}kk-Xb|7^dY zn+NYJlD0c>Cw1#J&js)7hf+U#TJ653@hz`dxPyIG=$?KS*gOwKe2Fu`n(e8h=F%#G zz0SVdk6XXG<&-9e@C`)l0}qnN6dYyX z9uMvtZ~JQYI1+g2E-S_%0Tnl=eOJzagv#iRJO0|Ar!~X#pkZs*S(Q&er20SdGx^{cW-5#9BIrrT0Ajgs|R&y-3}|E zcc5m^2HEMqnOc2ke@uy)+hsNf7Mkv!6-Hjse%C(SF(^y6hBL8xB4iySux~4n5ub9@d?VfxjAg z%(;Mk#n<^9iJ4TSwX=1edMT_u*WJBP;9VCF&(6%Jw&%1`Q!NARxb>#r=YRw^4F0Pi!M+n5QwgW>A*JF4Y0W z^ljx%Lcr(5M|GmtYz;81_a!+&kPw-_MODW7ldSK0g3GHjq23p+%I+V%hCnssnJU?} zY$rE3Y6YkMWOQ8=oWI9jJZ0b0)M4iB3Fse&ioLNOAy~9{{^i~E*a_#3M~2j%>9I@b1hjqmch30i&^sC^%VFy?R8WaiI=(6Qt9I? zZS>lRr2j8acEhMR{c%>bo*C5yH17Rl5aMb>4QKR!q&S`1zsmc6^iDXVWi^t8WzfY{hn%AC2Yg z+l)_7Jo6t2yck^wz~f_KsBb7D*53}#W4Kxc(M z6In$l+Fyb_=UyeoEP5?&zQ5;gx<4WFA`*p3%2U`@@cvgbxD5pz=vAmVXuk&y9LP7K zx-&>~jyapCa19|kTs^ANgTkzaHj1~Hl4XyKdY4BKD#5Z85#`cthz3!EP5{;@*f0|E zw6fG9Qvo)677GD_@t1<)2`|JA4$XTYW;;t;D*mtLDDmJ7pl49ZWO3it5;h1Np8qL4 z^mT^Hq(=&2=Rpy5-S$(ee;8ooRNs$-{!;|LUe7P?I)kPDWyYEv9-+sLTLe{9y-<|z zq1mW$|F%V2k5Xm%dZAkV__R`Dhcv;hf z+)d^<7jcN`3U8nZJ3- z#a0O3uAgiCsM^0jl!+MGA4@LPlFSuDrBowE5Vd974jaxzapD5L{c+u5W7)bCD0wsl z2BWsS_&;qRyZGOkhE@VG@@Z&#QZLMfnmUztbNlM*k14bCZU1&On+B37+77F3Z#Vk* zEi;yG%oWE6j1FSLzHU9TvZw?@voIFbLt!5K8+sRL3}_*Tjn;=78FR(zyKYJVik;Lu zo_@zOnvnRMdeoR|t&(b9R`l-eYl`n3*gBKXVsbZK{FqNu9!S5Wyf17o{GbLXW)=DS zJ*?I!v{7&&^r{44$^VjhE{F>hc$#|?)P_7B@;gMuha#90WU*Va2&x)+V8uYu2wcAf zyNU^^qUHC;QHXXh$db)$EH@UOH5;J>%IHv#0u?O%$@5fOf5f`ZkcN49>*#O0J+d3D z*cuzLBURqvE@*d}>AFf(_t8ucOfHsxl-EGk`(39p1B`QcqHBLVSG|mgfzQmLGelA& zi0nT(kJv%}RGU3fnI)@@cCQ*IYz_hT%53Lc-U1 zNw1J+j_?0te*}cqJgYv#=U*nA_p23_-G4g0LrN0PxsbM)rtyN+eSmD}t_=n#JJ(qR>kM_gQ6rRSY9mTlMBSw%^`yJ7R_O8kOu;88^FCX3I zo$3#Uz5;5oYZX2@SmvZ42NIAUcA4i}Z2czc+gPwb?F8&Ch!wKL9g#GfcF_c|v`1LiV+U+fs#GXr`nO&xpW>S{D z4AVb|0Bu7ffkZBk| z-7)lgrn;-r%&>`a)%NUl5}(K#KzfBiFzt^vMV0w4USO~!jFNBOE zzrTlvrFn2IZH!y)L2TW}WJ zy^}hw|M6Ry){xD* za^r?$Ns$kq)0XU;x0yNaV< zn4|3z7NXOq`?^_+G=U-v9`j z@_qqSyN*0*Q-;;10#}sh?%hy2f&Tz_GhXlXk4}NbI%TtYQrLTV=WN~q+(vRlp2=8E zcbCYhuyf*Hy})QAmEK1EB__>E-67rEB=s&~7T(ZP&s`K5YK4#lOX)-g z>tBxJ&t2ER7kX@wC2}w=1M|~Kd=8e`Q8yu>`Z0PJ)+ZO}5+VmH<)nu9tXGOVKk6N~ zR(9@|h?!2;zWtDLMiccR|DkB$vaLX3aBsp{O=rHO4(`3rjAltOANuEzP10AVE7<#7 zYypM~kh&Dw=V6{MI0=p+u`YZN$WW-A`H%njM1Ci>#&{}FyjjQH+H#LmlLBGqK&raI zM@<}ui46b0uJ|AZo2^^NzZ>v=f}hrX=-rTKZ|?3~+8>E;@~q#m zA3Ay%WxY55hAyzL=ctt@(3i4)FHN24w38>es-EGlxIv^Zi25yrnt`WkzMkmNF2-aQ z*x~>0hXR}z_wroSkJL=(=HH>?2R`<@j#%@NS7c>=+eU2Q_m8O_=YLu?XYes4DkKXR zEjIQkb!V_znzaEl(8x1KC^FN?6%D>+_>$ev>Sr>9dd z20?L3htShvYAfQPc|DO>OYB8$S7zYOO3Zu)rkTpF63a(A?6$|2FvLgC>f~%62Tq>~ z4mOnf2OF+)8lIcc&$AYLuAd|%p0$3vNmmA<`Y2{tN9WFPj_d7PS zIuCW#S9E^=MO2}`5+Bpt&hs2auV`FSKg!W?i7s*+TztULtVA`?{WG^<;~;=^FvRmw z;2h@q$crkSOsN%_MnYk3_F!?k2w`044?4w1)BP?!2MN)CD`cW!qAy80kFJtY_9YJ`-JqQeDJ9Veu$FstQ=7~4SMrNmo@+S zsTRr7m<2u!u)Q&LAy@5Zq|W?XECYPtaBINbT^$$!(H9pK6)@O%lw6$lXJEdh=&)IT z_24X^m{KRqHG^Q9Ws2w>Pp_V#}tDQJIm{wSFf7k4MPU*Rm?+KyhExi`4@@cT>lvYW=7t9Q3*CG!I{#vPeJzQ$undpv}yj8A>^j}#CZ4upUbf`TG=B-4@@ z4oGxZh?Mw4#egZ3TFwo@59={FpY|ur`Hyv}CQ0)-4zWJI5J=E0(e;}9%MpE>ctUEr z*9q8Z>IaaK)CwG?hHA;fBMUQ zQyLZxZ%7R~*tzG#-U(m?WnLfa0IgM%wvWgi2}f~+X~jW*Da3%DHqqM3Ek+g}UbOU_ zN|eRR!>B`gNadaV(*g8Fe{rD=DiHgZG|Mf?Ab)1beaO_FfJ_B_>L0bPWmUh$^#uT) zjAU9-Uu1)7aBzf0dC1i*mZ9xun*t$T=`^&zM|rV-t+N-R77yVUvRLpKNH~zDtw*xi0L> z0`Fy$1^)i95))^&%BMunF(kDVXPxtR5I$Ej$Rzo@8=8JGXSQ^_6=bwaF9S0Q@8$YC zJh(!v6b&9ulk+7u+hfnhXL2ffdZtyr5rI zlR?M;rZWxdXEBZbTx}n+NvpX0I#(E|SA?@t$uU0ak@Nrd2&+-HN)=K+W%ED#d!uP( zXvsRDs8`uIJ#Lx#h&mg!;bmg1oI7LD7I^?0@0H>wQHf$R{y1ziGOc}a{i2R}s+d4k z;J&ibB5!mMf}fNvee;WX^iD8cLOQ(wlQygr^3xaFfqu10{8xJB)LmXqc0bOA_Vn7R zh9kXGo)f{)JGhE;Uc_7#nSA7dVZYevuVHRl8d8okLzQdWbz~s5)V_n3GB2O-MXc4* znY$mnM6~VhFA#_4W4~<0pWO|k)xIG12m5uZEJnogq7RMpO4C z?@lQkf^fI#Ox364$#1d5G=1s$X?ykv^X%`dHj(#u)FSXB3WT9bT;#*GBJp4*NDR4-SVh4O(EZctp=fA6lT*;Txp;XH`MjDG#8vg9L6O+-s5clfx+K+K~ z2g|l};R8vcK>C&G*!tG-6tnd8-_(vFUHIjw&sG>L5F;fGM9a(p?(zhmO4NeaDo};( zCg?eGcy76B>PvRw+cX?4MrB3%4$??HcnWA>d0pSBruq+q(T7meZ{*2vo67CsT)9V4 zvG98=+BDE;Vd03`1wNn$S|p(cQmnu+TyPp@lJ7g5RzW(z$u<=bRBi{n0^&W(!p_oW zvTA5RAm>!i*9`|&_f+8!vtZxx7(fuDzFz>}&xu{2YEjo&k^bd~^PTb`Wk=Cd(y^fA zr6hmZ_^2g%S+i+1M!|G(XrwbO0U#H|p_ZqtsEy$?S?81_r$@_dvup+-!2BtU?o*-W z;%T|wqNnD6wgnM7tmH?6wxCrqhU?>?`M@1M@M3y8=wu)Je-xc%R8(ynh0g@t-Q6W2 zAk7ehAl(hp-6G8(l2XzoA*pmoNsPctr*xMzNOyhn{hdFv)>$*>x$ph#eeHKkd@YOrx*GXDq$`lX=9 zq~?dVnpmfr)+JDMljUq(zor=P2@m~o@hDPq`!gSy$Cz7L!i>pR=s{t@sJVjp2CDxR z1y*@>-aj+?0Z*rBZBMDTuwa@ewJ3Nql+V@E1??~ph}(HC?$EheMG`KU=sR<7)%O5` zQf&$@wt?-xRD;#AsC@j1l)giM``!Upxwh-!YUWUSE8Wrkg;c-rPoL8?I(hMK%u`XO zNCHa^@`Lrk#$(;cp{Inxz1}2E?~&S2DQ;{5w(UTF&k@bK(Qcxp$`Gb1kT0p)i*7KhSRn@?$LVQs!c9 z7x4F^xKCz_pt@Q0em+-&A#emdZ`V$UU1ZUJF6%Uqb=l%!9SA4UZ5tbD5lF*?kz zij-*&eA#voJ4je=Dji^euRDt!bYQPY?}!~sKdv40?f30jJS>L;jwApZXr=^af`U*B zzyZOhb^oN&(;-tCfv7(XA@&fvrq4dM2s)7te!aP-_w)cFY>3MQ&#^| z-k@zS;Ly;0%Fz3Vbn5UDa|as-F(JP|&bB)JQf&13hQF`i<-L)km!Hay9Z~!4e&jFbAtn9ZsqoIH<)TKRf>NQC(wzm= zIUzbR+U&2NQGltTvHS3OJULC8mQg^KbsY51A_PnSTo%h<7IHC<}D?f+!n_ojz}d2D6K> z)Aqr^wm(WP`Si^(4HIbMq7N(Sqn!};Z zm;F$m5wK?TU#PrjU2KU2tbb{%Reie%lT3tS*K{DfzNgOgvbtBkG8DVB z-ql>mDSyTC7Ok>=`U*Pj>-YY!ivdqLOQrbd&y|@=SDi0WBz*ZVQS_*~B46VsxE}u< z2rKS!i}2aVE{(gBwzqty-g$M$Rs5hU2*bfB#Ud4Jkiq}BMWOXCjYO?lF?)#lQ{&x{ zNV?OsERHfh9;yqY_Z8A?XQlL$?z)NNVDi0~cRzlhQB!!8a-9V0o8L4=dZIv*85qO1)8yV+;!qQDpr+S;VLbgBTN42+;Yz!QeTC7b*PKy%7q=T9%$sx(2AM*@9Mr_M^DgkTGn z&K{?VfUu_bDe`#rYYKL#loGYk>3G!g#iV?L(I|Hd|2$)A>)b>lCtquHc-#IaZ#be* z^xXB-m1lJQ7B- z!0_81#`?gvTqK{ZNO~GF6Yr^$ea4927+&1yy1&1auLt-e9dm)+7t{FfxJNr9CXypkNs#Qpzz;3}ww9b^wWRPZ0Y3VV zDJ$(N|H4Ze5If~WYF4{DjP#_aei}j z?CV%o(A3L-9N>#%R9c@2xPnHpChI&_Q?;J!a>s3mAQs8-&b`137PPf5r$B$Cel;@` z5e-*?dUqJ(u6rG$Hut*)aUn%kk57R0MBkRPyURl$(L5FyW-_z(71s%U?L!*0|JaEH z(5c@`HH>h@ZkwTsv|AmEd)VnBMr5I}OXgev=#=I0lFWGs+iLKsm8y%HJ4!3B9z`lo zfl(7ME~hu==Vmi>V=A$cc~2JhP`y&R@x}5JT8%Sq`)`diRnN>eI9ooUP#`$5u{56N zmE-2Yw++=yV-mO%4&<`Yj(i}C&Z+@D-^b2|0}o!5QAashY2S`g6SGQSMHHw?J0%*h@IU%vhvOs> z99XPMLcqvpIZ;!QrPqat;Jq@b&(9%Pr9-KrYu{yovAlB5O?<@2q%=?GGulm1GW2Hb ztU3~4Mq5h`F7XYR#{-}6BgTe47~8Vo%wGuy%p;Q6!17j z1m=CWp0hyf*5$7=4Xsx`Q!3=bj|_2kN!^EoC$Dr?2YvvoFYyr)fp5`=SJ$^Ft!jSb zg{m~{V8ssy_!e?t3BE;=OKWL=9W@JO8Z$XUC?&b!%b2a6xJ-CI&OXRCoJF(TF#L-9YemIsim9Xifn#*ck*I%O@86lZG#Y@84Yh?7`N6zyy}Ab z^0osa_$NkZf6V0YL81M@9oK_R}+dy z0!ZsO{_fUzu9Rfmz`CCKn1FyFu9xQ-q1&{t7)f>T$3xV9uP`tc|C2nIZm@`)>Lsaw&{%X4P=WVwZI9?;8mvN(y|`S7`5ogj?+*8^;M>T63n= zit&Pf{Dx1zX5N=*?Y3X3#us7IGt}sKVI~%y)L3Ltk3Wu$;LE7}iGKe{);RsHCv_hr_3S=6En+qowF680k$Ll<&Z}=E7Vo9b+C4bdPD?5!hnyJC}$8 zy7|%wxnhRlf<6&L5!m@nv~vNB;I2XF?%A)^mf5Q6ctAxD+^-8<^srJ5fed6(9m74^ zx4?87FS~OQgn$SgeVSUbC$KAZyRATF@#p;!dC-YWaHCf0evpm7uBhp5N}~%zjT!0k z81Jog~EvcPE^2 zK-_Bo%C~OsU0l#A7~my%a2mVbXF7U3n7ZNMS^d}r^F1g0ZFI2+c#Q#g7<66J4!S%* zT7I#CW;RcOn=QiomK3YK5|vtRR0jxmoccnAI!Ynq+5T{;4a&aD+{ipz$~_gagii|) zd1xBwsrh@9=w|?KgQ6q!?63y0G^}TJ7mfl||Oa%|peE3NBdDD(X_gU~3a7r{Zjpbt5Kkjmo6B_@CQ^N=> zUE)=kqaHwJs1MoA+VE;LhD^Ru(Uai~PaZiUmgf%+SSm)^ft$xk@gXYTGfBToPNa~; z3_ammW)%SzyaL(gQNS8({pP{I&3j!S7s6*ig5c8N3AEFnB}mWK!f=mQgOkSQ+l_C?$~#GT2USlrI)H@WTW?p|;FRKTRKBJlx#DL53Zh#~(?X3BM~E`2ZjfdaEc zkYYjxzli2ULhe;uvyihwSQhD)byL#m%-h&E9PD7@l;NGvtne9@xsMbIb^pGI{0dOA z9*V>TD^vw9d9aTgu^(2c+qU^f(Q{VWcPmHXz0_nT#Vq|VKZcH5M;OBn;Kbpxv}q+S zuJg^vftF=$(?)dZ4XN$jUQkB9y)WcV!$<6S;96CJ$C+UY0Me9%j9+k=cDid~oW|Lx zO8{CZ2om67M+r{XUO-bb35ej0Gs!`3EINp)q66sbKk{Byg zajx*ZWpxTin8YvkI%qCge6V<;ER#b27}M3)$f40`{-v>*>W8eTwVNdaoQG5bfOEr) z#uQxe1y>kdcWSK0DCoKRj(3Uoii3r#qlecTw^Wkr!1Hc@Dfu^0&h+K-Y5N3c+qbZ+ zWedi(AOAKi6Tgve;vyxadpnD%o2Y@fD%9X)9EOR}X0|K8?rDv*^s1_7mR_B42^(h2 z*8*8k^_X>$C#v!jx}@4_Uvu{GzQp;jA<+jW`z+pNU+w{{eIu@?H0rS5oOh6KlWvj# zWKo>Y`Z*ViFxTEzg5hJ&?5iH=YyaluV}bsUc@N}IM@@Ny_j%9M9lG<{xIi9t?e!`X zAW4we010j($4p!XY%Kkg4sPUGis7&D{Qxp1NIb4ua`4W9I`XFBZKZb;FQ5F&?frk= zV*Of@302fJ>aLt8&)IgRmeXJtwI4|5XrP%R#oD9aR-`l7<3BU;oQYyryUDm?H7*(1s00dKhe-$swZ*UE42(I;1Rb{ zV7O0qU!M!h%fGx+UB90mkV1=SxGlJmwl@oAAz_~ttWs%FLuN%yD9BK7JSLrCLdAtR z4d$NU{)vJ(e8S2@gfAigIi@T%=tRE?9e-wSt*R}9wN?YcxU+R6!gKSInTo#f&pF}8 z?M_8IdR)-b*V#0#50Jp;J(UFWcmppW7&8lsG^GB;2%)MgwS~z*r_KSB1Jr(WI1y;z zFOq(ois9R`_A+Pe^ftwT%1KigR%ZrS5|hj2%N>^~vFH;E3z_*NGS>KbQjb^Yrvsn{ zp~mRNr$Y?)Dc|Py4Qj6&_8MlKZ(9=*Wof6lD`|gLt%*lj%Tae_C%I)TDLdZAjn1-} z?zCA1gl6m|^mU@(`r^`3vOCU9l-Y;Ymnu20yox?OMq3m{8PrDEuTF0B$$a|y5pRs3 z%iRF$b;-p$0cq^o!479kYk%<#+g@^Xoe9C`ctXrM*a{{m{7PPP4HdbK#J3c@ow2FP zB*DtxOV*?W^Pml2gq|BxkA3DJ7T_E?dEVT*yo`Gh5GZ%C5uOH;2&k~@Y6m)er5eT_ ztAla!UOa~fF40WNRf3yI)63Bjiq%+9ZCM}|xH;1w_uV4^1IB8lRAEl^#vl|rJ`Fzc z?50AV(6c|-g3#t@r*LZ3P%*mAJ5@vVsF7D+zl5&Ebdoc=_lMyxuuGmRIXOMdDwPKx zTVZD_$h0^vNAi}6u_~=Uzk9RblvcK-$o8NKwb+#if$~;gxx}##ffFMD`=?k|wIT%eknm%MPFUnofkNk~B9k`d&{+LgRdnQPvm=Wmp{knP} zv?HMPO#9AjZE;^w@oU6cU`D745$Q*fHkCDw7+I9z>dlIGwwKSx|1s&7Uc6@pN$u|) z71EsGtv`}^-7gb@-Jvj906AG_+_+eq>mYg|eSr46nbllsK@HCUm%NH@X=ljAYbu0znBh?Yu>&Im>W{uA+~i ze$S&sihHdv887T4FN=U5%Q?Z-V$eBrJS2n zc5hCxYA0c(tW*i;S%!z#J^d_L7;C{z-1_BiiVZpuK&a=Ny#)M+2w`bK?;+gn0Md7) z2z|NH<=lXY5%gA!-5{rJs9oGN`3Oyt;Tb;O2dR-o(fW| z)fxz|WECyM?oI#0Ro@PMQ~K9L={A=2xgiMb9mv5q?`mT2`Kw*uyDWH?JkO+qt%Sw? zjnm5CJza-R$~J7oK!^x?MAbQqF03|WUlcxKFuLiM@FVW@6`S$Q>E`jE1;IB9YAflj zUk9ENVgaPeTAlElqcF~sw1I>ycSDb>E%QEH`dpegXt8}Xz0B*TK+CkR)oUS^C7ltZ zw4X5W1!WTN!AMO6EvJ}ce9HCN|MvLFGcuuq`l}aU#z%y&4SEutC87n!1z?6JL?fUz z0T^8kMIPy;cXyhHH^2N<2rEToMfikLy$Z+a`qVcu&LCnEc-YUzASAR(I zrvzHrUfl0=Vkk$|j?C7cL}Ig_{-Op}-nU|6Xa4?Od>hX>P~Nz&sw*vHJec0=c0}iV zf#9i7RwIPUuo72BVEpz%V|Qv}s7h?pTIG)37-w3Naj=jx)cf2yjuRi3_*S(gT+WAE zb61m9={d$C9Gpl3-WI8*+7-zx$hUuQtPI3qW)U;8KhO_^Sa#sX;Chh&k^LVr5^tZa zsB`~@db^w9~olWCNYutAHQ)ot3TrO28QN6{Kc3k&L);0R%l5 z86RvV1bMK-=aX|V=NwYa7wtlgT~pCu8+@-;(03IGf#o+D6@gZtz7z?ac=i>|`2C>< z8l(WJfoV@kcV1lKFCem|7~`4fL88no6O`R8m!55Rv$m&(s`?hxydbIG^ z0JEQ$UiYrv`MQuf+?m@kMi-7%K3KtjjUt^!&^Zm%vG^1lBKlzEesrC?%y2HkGOQlg zxb2vX%YnvV$rwg^zAqzK-hYQAnE5YWasa~@g@g-xeQ-B>^xvEv#3V(uocT8_G}ZCo z3`NsiI54%nGtaZe^^0Pf7c@c`C!d5I0Z=Lu{3yF068i3T53uLq>-ih0XMeDjJSdEHPV2j z395vyVT_amBGia#A{8v}`lUIP`Y&qgmK!5o4}!*x4QWb3B&z({6UZs};ay2Dol1Fu z=%ohXEdA3PL1Yyj(gbi5Bw&VquF?5aqKht$9Z`Y4hx57j`+Q*MC{H3(lHs|Y6j62D zlEEu(?oSyagSTBqnkcQfj9N(Cn3sHJ=!&88Ta2D%x{`mX>`LOggnN99h0VtF2q$86 zAG6T!%?&Hnexm6c-}4`9L~6xZOW(s(Cc$N{D$PH#zJz=)s`?WnHml{teVn)L7>L<) zEP=vP!_cEmIgq3h{KuUfC+FbZx4*B~ z+sUL&(n8qV0fRP*L?V#s4icko?W?YX@Bo{kxZg%@T}r25Boe^C_vo0tU;s+NSDb(h zATe@AT1N;=0&dfC!a?qiw)6khB>K5dY3`itjI)v&^$Bvb{78B7pEm}<-wHb>suoG3 zE*B$$W$KSr;B|6{Zs=U4#fqaKO8j@R)w1v(c0{!9r=Pytj4dhC3Tg~4{@|xU)&}bH zJ6^x97!x6z(!uA=UfO=BmjH78g|xPJ*6>3FYy}MXdDA zgG9mS!=TP?f=4-rM#_7d|KE*E@v^w)X|Wk&o=A&5_=%ZQg3<(yg~M#|GydID9*e3P zz&Ju}N)z$iXR?-iSnC=BbH3te$c@uR*ji^L1L4j8rtg|uJMM^rZ$y_iOHbp15LpmW5cF^Z{T>MGF<@4FWe&lFhFgG`0&1tV^0UQS3S;fw$@sk71SHq zWYR|rF#UI}f4C!U+#zm*hpf_i*g6A6OyG~Nm=D)`f0o_Ths9=NOm-kVsUr_H+umdB z-q0VYG!n3Wi*wO046CZ9Bc94%(*YHi1uAM9#1@>O(J2a_pbGg?Ij-J020;R&nFof8 zT%hm@OrUbOhO%r#M_1PL@xqnV&2A05+-WA^K2VA;7Ds?9DoY4=zFNc2Y}jJBcHsDn z#OE=~mC)lID7H`})RP|x8K~H|UD~gaw8Q|}SARB4Mz%H4eWQd`?Emh~`I)xE4BG*sBeKaYF_KdfHYWIS+5`smq37U#8Qf?vWievL zt>`aoKS{fKW`;f#n~L5)e!lcGZ^xbT4_J2ebxDsre9QjC5mOx&r1zsk-=M(^qe)ir zN|ok=B=G|C@%N~5+PI+RO;N9Vy9M)$uKuImT}<{VX-mGbmX2Io5eKN?}0@MTslq!DWI0GRRG3V{}nAF~^IV-r$U&|EmUHabxXKbcO{3ZB_1)m;c zzE~7z*IInqhc@rsymL?lkv2&E&zpD}c^ROMhTg3STdWF5WLd(uZ#JjP{R^}uQ3KI{ zFD_!of&qy>!9z6h1gwMXi>{DA9WUTvIvL2Di94=%?VeuA%>dX!|3(15U#JF^;P2b# zeO%Gm9i6>McT^>5jaV*Gtf@#qmv%?lmwq8f@9pcp4*4jOAfCdpKQ<*$!EDbv+Ve8R zDyrPWDF}xWEH$5<77H?PCkUd>O^PV+-?Sy;m_>-TABRg(&2vX(s#Uxb_6^WUtF0;& zm!s6{>{JUaOB?ZNsR(?HG$PnmKl=T-yZwla4jYvp)c2!R} zdZpuJ`p+Ri-~JR~Y)pqyDGp)Gyey8E9?Q67s6<;Z^*c34w}w~UK0t=`xr>;P$qi(HugZWHmzb@i?zzFQ~0 zmO8R7BKqe8gNG^eDN^7Dj|**(S5PGG+z&K`ed;^YKnYI%?^L{CY4-H?Jw4X!j_r=Q z`GKFa86G>=4JVTBQT^9~=bE3aA6=EEGGr<{R!?qdoqOnNtYt144b=Sy=FSB6x5nwZ zzs?H5<)xD6Zdo|rMTf5Y##ELX`hg6Vm%J2VSfHhxsBgp?QE;$?K9=xSE=X}f`Y zwnufN8K?Bg6kh+Vn8nkcY){zxl0_nW!oN5w z+0muf=E?+rrX+rW8pqI4xlLi!9Mu+{{y*NI<{EO$G7SY5-Bz^Le>PSrvK-`E9xI1a zBS!Lm{$|S|zhlS#!YuAxB&#JnO@Vf#}d&YG_=vvBav zDjq*G$eY#Aib)YGRoFm6f{nVz4!a`=$(=i=P8vZrd7wv^pOns-ahbK^Y8br69QoRP zyviZuYCYr#k(`ZJ>fJ7F(&qT07~@C`w3q`4a1Jcv@kZJuT$CYEh0*Zf*~f}VAO~l> zYJT58PYSXF;K@6F@q(?etz6jRV ziPijmnQjZqf4!4_OS^N11cOQiV{^4ENtVh&*^&F}PwW~u)W16yJpHuT@C>LwEf8$O zxyItHP7Nl1E?Neh(BbCD3b*$dXz0j{7TJaXH$7Yrl=DeXBb)#p{Rw?nz#(GCnw3u7 zB8Qm-@>VN*nQ>VxQUuR2Ribj{&-a$dPgb7S($}~_U~FEr6F!j73vwJkEki$wMX?0t zJ-Ws8)|h{!cUMiqQkc&Bli9Bsxl=ibZrgR#({BZp9S@Z;==>+(ws(D+HC+igAoyV> ziv;k;Bg6hmMdo9~Rh=l;0w1#6+kq!F;2H7Gj_aLa#L*S$hm(pk2!=1fNDchQz&W_G zUz2UZPIIt30*U!^t=QETpn8f|ZC)AwzN=BS5j>&H3umANoqmWR4ZwxFh8X33$Ev#? zLvC%1iKI|C&iZ5q)3fXE=Tf5a177n2Z^$RI<@sj~b7cyPM`2IksdMYr7tOS0h#5wz z2spl^?+`w&XBcMyF65PzkhavSKo6?tD3cSUjp2ptGg~jvEvy1%)<}SfCr>|&r5vg8 zpjuE2G&CYF5|FJJcMHW?DO*&tU2MHL#06DeWzRI9WrV+Sa<0zyj2@Xb|JTHi4%VxV z{x)tS6;5C=4zAjJerxh}wtzw6ZV)$v`uX7{QTo&ALj6^L#!dr>@t^t(F}m^|Q`Ok9 z>VETle7<^zX@M&&Zo;$^eKQQV+;8XOm9!7sEJtpMk<#XMQ@7u<$k zN>nVIVFJvk@#CUcxF#w)3)~j}q+BRN^V8bOxNY(;Hog~8SIMR7KQZA$z)JlwbK7I& zE(-k_mHl6`&bpwWL)^_@48{SLU}v>&jKE_|{Qu8GOKx=Ai|OpEQNEAXB2LltZ^RHb z{Gj2@ro&NRE=F$$Eh?9k((iwpEan91G0K&InnBOrgNxs_%7<+q;3w|Zw29Dvo9fQt zy|q-=R4fH9jPDGOW6iSnK$`dW}S-3vy!l3%jNq3@_JLc?xn0uVI4#l4$wq_xIJWmJPp48@iM|J!dir z{J}$F3ey6HRyvo*G1^J^)LoJwlhQLPMh$xUR+}m?`z)XS%?FN&FM@?(xKn66kxfVZIsv;;s4^%>3_PLyz|yem*}vZSDnXzuDfU?CTsU zhEj!hZVVU3v~R-i-e!_b>Aw{@ti}T&j8`w@o}B)(&HV;wmVV=5X}~33)1wJs=XdOa zH2=)IXdS?h6tw)k@tk&!mWt`y0e%!$=G#BcUqcCVJS+*}J8nL#78UsLb7d`>$k{w^ zW$x<7N8cAK_=~&+P_e}NuWvu!`pUpkFD6}Z*w6-hr2Mo5n3yX)pb0m>MiSNW8G9F8 z+CfezA>}UXn^BC?&KMkTm0JGB?ft*)?20<|7pO6y$Tt_ngN$!Fqw0k7@WnrJQLPDJmb~SA#Un;n z>W4Y!)p&+m04e)N2>U56v`sneYS`HHW!|*u*2y+cVPNt2X2-{X_RK=w>f%W9!0@?4 zfpB9q$d%*mb#{z+x4&DW4r~n4;M2*h0yn$s8&E6EH=Duk??zRf=TCK8fXW>4J0Jh* z?uy{?8nXYsQ-kZVj>&*E0$=EJnMBwpLckJQc2_*6;Jk;~gQsKz7H7ES` zBEwaE=&9YOeGJ6S_BtgJU3lw&h?Y~?QffV7K?d*;JC7E zG-xuhk(0Aysppn$ZLEy8e>`@(F(mrRf_=4sRMn29Yv#0x;!*OW!mi&ckL>o&ei^zT zer>y?#d9PJNkSx`;FY;zJG)3+96aT+aL9v>(jURmSYCE&FZ@x@_aFu`9LgQXzany_ zE|~NI#X7Thh>$Vk8zwTrA8zR7+9#n8icdH3X>qCiqsqBv{%GV2NZrjITM&WYaBWxi z-L5VHWq5l+tgzrp2|$P#!8ig(9%XVsLhNzq5HOG4ya!`e06t+ywd9myh+-rnKO4*@ zSjy9Yj8)+#XZzm3F&h`h93^Ab4@O-s?aLxPaw@v|Dy+LlX_)ArA5;`6)^dwoJ`u6F z6$=TvWV9{j2}>-hEd_ws|H@{l7SA=4pwd+v`^W)KnNDHusTIGg0%2hgTN#5YxCBAH zuZp$te+$J%1t`d^a?>x=f6LjBjP%5+*cLYU8zTtG znjL|g7@i-rv`)SA`&v;E_6fy3OM)QLM&QQ$PcaiF35NPq*WH!ebnLJFOQKZkDk*c( z_O=s8z32!vJ|8x*x z(?>{&piivt{M-C}E2>!NkJ+oREgZ)jI=fZ1uR*DXZ*l+Y=X-A&w|xbF$6_!i@EeE4 z_>Tw8&Gh>)PMqlBWIk2dx#tUH5Nu}HX)VamngXPUYCryiB#Yc3akxeKdT}&mMk9MT zzrm%ToL;_6+Mb%CwUoae(_$a@0n8=parG>)Us6d>Wdk4i#**{6FSHR*4JQXR2Z&A5 znLXh$VT{WHA$KdKsuERRE~|t6%i6dwdLZ@xDv=S&iDW>Ku!H#8dig&~sTl$WkM#RG zi3|YQi52~S;V4Z5j$i02gwRMTtdPs%FF8QSI1|RS4z&0`5}SowUT|r=Pwi~O0gVga zjx&gpdO`u1*J+t`Y>7wYvaoRP!{Wy;+C2p-dlPyqO2+;>R<3FvIo}WdB*3R!+QTnP zxiN%P3}ij4#OI;1MZ^@L`Y$QB)A{MSH)_CWDLRyn+)}12>%V^Ymh1Cu7hylV&K^^t z#DJr@Uh&Bn=lK)c^b$^|&7s<9V1(e?o<>@W_uz-z{yIe~P^G3kg z&yOsVKXPZBF*Chf9~j&}#~Ddt(G?m}zl)+vr*M%k1Qh1-|D!FN!Rhhb8Tsfs<`WCx z#A3NAkG_N~umvB++SSzgsRRPcwaXxbN#f%-B+_CAdf96*y)3rR8;E-tyDHcu8`Sw_3lGlnjJi9_Zcf_;uDFVuIXHeGnVB&x(zZssp6M^aCV? z753r(;C){49iXO8`oD`pJ-9dYFExfbGic50Fq-9Mq;eJ9&sS2?l;bNaJyfbQWvxs1a4WiWj<=W?bDU_c{vhR|1m3 zg1Foe)7Zw-jfKf*XxGL-q3PQn@Axm?>lM^{;;bi{Vy=!G?giB-ebU2ORtt5Cd{Zf( zSD^bVlGo;!8yD)K;`b{sT;O|*)j1qw^6ngh2`g= zM{WPW^AzMB`wO&^;tC&z-YoBUojX4-x=FvT0^276ohUdpZy|{PBtzWLOv-^=U1GRb z9IcZSoVV~ScL;b1wJ3&T{hw$evLi&EMx(xh&F=GJ^v-0_5= zY7!#+H)Gg`Geujy?BXMSID^CB(SiubxYK{o5c^Hsy9_yI?YCGB2#IGgxLw#Sn(k7W zcZb?iIc5kSA@^bz9sWF;qlq_y^;b*L)jS>Jn7WfC51Bai!Mg^ zmNK=%nH*u5mNA}EiD!ijL(2p@rdhGEUazD-YHS7`(1G(>N19eMCJ=16`VP$j1r`+p zyg4xZi;tj3-6oG@N3kmAS{slE@sp%_$lH46Gs*3r$aOVm&<3q8cl79ANA2K=^By-n zZLcz+t%W=u+MNuUccXrLfJMXo@gkA|>3i&5BXNt+CuwtS&@XhN{P)IU^`H+`GtY*{ z_2&Y{B!CC9WPDKUCePlzPQ^A^7M4$B{&If25nQ!pmQAyog=bhrQe$&@^Nh)~miQPW z_EMJtB5$%VwCJ44+4`cXD0qi1$S>XSxHnuqEbe0ub3<1JG-{air_69NSu!G}!}r1U z@D6WwWiEf!prt~s5;_Vchxmua9qo{k$(SM5>T9p978z zroCDm-VL|nj9*~^n8YI^J%U)WGN>HHDMDFR=w$;nVp|}Mi�pa&3b-AIkMq>qIup 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