aboutsummaryrefslogtreecommitdiff
path: root/mod/src/main/java/kr
diff options
context:
space:
mode:
authorsyeyoung <42869671+cyoung06@users.noreply.github.com>2023-01-13 13:11:13 +0900
committerGitHub <noreply@github.com>2023-01-13 13:11:13 +0900
commit87df9a914624b295e3e93db621ed2a8256eaf846 (patch)
tree538414a186e12b7ad1846462e8406c761a156a69 /mod/src/main/java/kr
parentd3f5b4df809fd03919952285a1906652097f00e6 (diff)
downloadSkyblock-Dungeons-Guide-87df9a914624b295e3e93db621ed2a8256eaf846.tar.gz
Skyblock-Dungeons-Guide-87df9a914624b295e3e93db621ed2a8256eaf846.tar.bz2
Skyblock-Dungeons-Guide-87df9a914624b295e3e93db621ed2a8256eaf846.zip
Dg4.0 guisystem (#269)
* - XML Like configuration for GUI Signed-off-by: syeyoung <cyoung06@naver.com> * - Stylesheets were never a good idea Signed-off-by: syeyoung <cyoung06@naver.com> * - Ref's are always bindable Signed-off-by: syeyoung <cyoung06@naver.com> * - cool components Column Background (color_ Row SizedBox Stack Padding Signed-off-by: syeyoung <cyoung06@naver.com> * - Update copyright header v2 Signed-off-by: syeyoung <cyoung06@naver.com> * - Bunch of stuff Signed-off-by: syeyoung <cyoung06@naver.com> * - Few more elements Signed-off-by: syeyoung <cyoung06@naver.com> * - View!! Signed-off-by: syeyoung <cyoung06@naver.com> * - Better click handlign - Popups Signed-off-by: syeyoung <cyoung06@naver.com> * - ComponentCreator for DX - test view (for testin Signed-off-by: syeyoung <cyoung06@naver.com> * - Column and Row shrink to crossAxisSize if its max is infinity - Gui test. Signed-off-by: syeyoung <cyoung06@naver.com> * - Load attributes after super constructor - Fix Stackoverflow in RootDom.setCursor - Test Gui Command - Test Gui to testview.gui - XML Parser settings to ignore comments - Controller check if element is actually an element, not a textnode. Signed-off-by: syeyoung <cyoung06@naver.com> * - Component Parent propagation - better placeholder Signed-off-by: syeyoung <cyoung06@naver.com> * - Line needs texture2d Signed-off-by: syeyoung <cyoung06@naver.com> * - igitignore update Signed-off-by: syeyoung <cyoung06@naver.com> * - gradle build file update Signed-off-by: syeyoung <cyoung06@naver.com> * - Flexible - Background uses singlehcildpassinglayouter - Helper methods in ConstraintBox Signed-off-by: syeyoung <cyoung06@naver.com> * - Line and Border - add updating code Signed-off-by: syeyoung <cyoung06@naver.com> * - Textfield Signed-off-by: syeyoung <cyoung06@naver.com> * - Column and Row with flex elements when given maximum mainAxis now throws an error - Column and Row when given maximum mainAxis now shrinks to children Signed-off-by: syeyoung <cyoung06@naver.com> * - Better architecture - I'll document it later on separate repo Signed-off-by: syeyoung <cyoung06@naver.com> * LGTM. - Might change rendering architecture. Separate child rendering and itself rendering Signed-off-by: syeyoung <cyoung06@naver.com> * - Fix some typo's and issues Signed-off-by: syeyoung <cyoung06@naver.com> * - make it work Signed-off-by: syeyoung <cyoung06@naver.com> * - smth Signed-off-by: syeyoung <cyoung06@naver.com> * - Button Signed-off-by: syeyoung <cyoung06@naver.com> * - colored button - passthroughs Signed-off-by: syeyoung <cyoung06@naver.com> * - fix issue in text Signed-off-by: syeyoung <cyoung06@naver.com> * - Delegating widgets and scrollbar Signed-off-by: syeyoung <cyoung06@naver.com> * - scrollbar Signed-off-by: syeyoung <cyoung06@naver.com> * - list Signed-off-by: syeyoung <cyoung06@naver.com> * - popup - align - stack now only propagates events to first child (lol) Signed-off-by: syeyoung <cyoung06@naver.com> * - Event propagation changes - some presets Signed-off-by: syeyoung <cyoung06@naver.com> * - moving stuff Signed-off-by: syeyoung <cyoung06@naver.com> Signed-off-by: syeyoung <cyoung06@naver.com>
Diffstat (limited to 'mod/src/main/java/kr')
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java2
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/commands/CommandDgDebug.java11
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/BindableAttribute.java99
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Context.java31
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/DomElement.java242
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/ElementTreeWalkIterator.java48
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/GuiScreenAdapter.java284
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/RootDom.java74
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Widget.java94
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AbsXY.java69
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Align.java64
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AspectRatioFitter.java87
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Background.java61
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Border.java140
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Button.java127
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Clip.java70
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Column.java193
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Flexible.java77
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Line.java122
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Measure.java59
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Padding.java80
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Placeholder.java66
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/PopupMgr.java68
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Row.java196
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scaler.java99
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/ScrollablePanel.java136
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scrollbar.java166
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/SizedBox.java72
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Slot.java43
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Stack.java98
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Text.java214
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/TextField.java433
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/UnconstrainedBox.java60
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/Image.java63
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/ResourceImage.java54
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/Layouter.java32
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/NullLayouter.java33
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/SingleChildPassingLayouter.java42
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Animation.java38
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ConstraintBox.java40
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IPosition.java25
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IRect.java34
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ISize.java29
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Position.java28
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Rect.java32
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Size.java28
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/DrawNothingRenderer.java29
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/OnlyChildrenRenderer.java51
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/Renderer.java34
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/RenderingContext.java138
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/SingleChildRenderer.java51
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestPopup.java38
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestView.java51
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedExportOnlyWidget.java86
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedImportOnlyWidget.java124
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedWidget.java149
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidget.java96
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidgetConverter.java54
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DomElementRegistry.java91
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidget.java25
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidgetConverter.java48
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ImportingWidget.java29
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ParsedWidgetConverter.java43
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/PropByPropParsedWidgetConverter.java140
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/StringConversions.java51
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Bind.java30
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Export.java30
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/On.java30
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthrough.java32
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthroughs.java27
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/Parser.java25
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElement.java34
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElementList.java26
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserException.java38
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParser.java70
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParserElement.java72
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/WidgetList.java26
77 files changed, 5931 insertions, 0 deletions
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java
index 0bdcacfc..9dd515c7 100755
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java
@@ -38,6 +38,7 @@ import kr.syeyoung.dungeonsguide.mod.events.listener.PacketListener;
import kr.syeyoung.dungeonsguide.mod.features.FeatureRegistry;
import kr.syeyoung.dungeonsguide.mod.party.PartyManager;
import kr.syeyoung.dungeonsguide.mod.resources.DGTexturePack;
+import kr.syeyoung.dungeonsguide.mod.stomp.StompManager;
import kr.syeyoung.dungeonsguide.mod.utils.AhUtils;
import kr.syeyoung.dungeonsguide.mod.utils.BlockCache;
import kr.syeyoung.dungeonsguide.mod.utils.TimeScoreUtil;
@@ -238,6 +239,7 @@ public class DungeonsGuide implements DGInterface {
progressbar.step("Opening connection");
+ StompManager.getInstance().init();
registerEventsForge(cosmeticsManager = new CosmeticsManager());
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/commands/CommandDgDebug.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/commands/CommandDgDebug.java
index ab69fec6..5b553bdc 100644
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/commands/CommandDgDebug.java
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/commands/CommandDgDebug.java
@@ -40,6 +40,10 @@ import kr.syeyoung.dungeonsguide.mod.dungeon.roomprocessor.bossfight.BossfightPr
import kr.syeyoung.dungeonsguide.mod.events.impl.DungeonLeftEvent;
import kr.syeyoung.dungeonsguide.mod.features.AbstractFeature;
import kr.syeyoung.dungeonsguide.mod.features.FeatureRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.GuiScreenAdapter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.RootDom;
+import kr.syeyoung.dungeonsguide.mod.guiv2.view.TestView;
import kr.syeyoung.dungeonsguide.mod.party.PartyContext;
import kr.syeyoung.dungeonsguide.mod.party.PartyManager;
import kr.syeyoung.dungeonsguide.mod.utils.*;
@@ -452,6 +456,13 @@ public class CommandDgDebug extends CommandBase {
} catch (Exception e) {
e.printStackTrace();
}
+ } else if ("testgui".equals(arg)) {
+ GuiScreenAdapter adapter = new GuiScreenAdapter(new TestView());
+ new Thread(DungeonsGuide.THREAD_GROUP, () -> {
+ Minecraft.getMinecraft().addScheduledTask(() -> {
+ Minecraft.getMinecraft().displayGuiScreen(adapter);
+ });
+ }).start();
} else {
sender.addChatMessage(new ChatComponentText("ain't gonna find much anything here"));
sender.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §e/dg loadrooms §7-§f Reloads dungeon roomdata."));
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/BindableAttribute.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/BindableAttribute.java
new file mode 100644
index 00000000..2070aef3
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/BindableAttribute.java
@@ -0,0 +1,99 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import lombok.Getter;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class BindableAttribute<T> {
+ public BindableAttribute(Class<T> type) {
+ this.type = type;
+ initialized = false;
+ }
+ public BindableAttribute(Class<T> type, T defaultValue) {
+ this.type = type;
+ value = defaultValue;
+ initialized = true;
+ }
+
+ private boolean initialized = false;
+ @Getter
+ private final Class<T> type;
+ private T value;
+ private List<BiConsumer<T,T>> onUpdates = new ArrayList<>();
+
+ private boolean updating = false;
+ public void setValue(T t) {
+ if (updating) return;
+ updating = true;
+ T old = this.value;
+ this.value = t;
+ if (!Objects.equals(t, old))
+ for (BiConsumer<T, T> onUpdate : onUpdates) {
+ onUpdate.accept(old, value);
+ }
+ updating = false;
+ initialized = true;
+ }
+ public T getValue() {
+ return value;
+ }
+
+ public void addOnUpdate(BiConsumer<T,T> onUpdate) {
+ onUpdates.add(onUpdate);
+ }
+ public void removeOnUpdate(BiConsumer<T,T> onUpdate) {
+ onUpdates.remove(onUpdate);
+ }
+
+ private Set<BindableAttribute<T>> linkedWith = new HashSet<>();
+
+ private void boundSet(T old, T neu) {
+ setValue(neu);
+ }
+
+ public void exportTo(BindableAttribute<T> bindableAttribute) { // This method has to be called by exporting bindable attribute
+ if (bindableAttribute.type != type) throw new IllegalArgumentException("Different type!!");
+
+ this.addOnUpdate(bindableAttribute::boundSet);
+ bindableAttribute.addOnUpdate(this::boundSet);
+ linkedWith.add(bindableAttribute);
+
+ if (bindableAttribute.initialized)
+ setValue(bindableAttribute.getValue());
+ else
+ bindableAttribute.setValue(getValue());
+ }
+
+ public void unexport(BindableAttribute<T> bindableAttribute) {
+ bindableAttribute.removeOnUpdate(this::boundSet);
+ removeOnUpdate(bindableAttribute::boundSet);
+ linkedWith.remove(bindableAttribute);
+ }
+
+ public void unexportAll() {
+ Set<BindableAttribute<T>> copy = new HashSet<>(linkedWith);
+ for (BindableAttribute<T> tBindableAttribute : copy) {
+ unexport(tBindableAttribute);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Context.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Context.java
new file mode 100644
index 00000000..b05c1e65
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Context.java
@@ -0,0 +1,31 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+// TODO: get rid of this and change it with tree walking.
+public class Context {
+ public Map<String, Object> CONTEXT = new HashMap<>();
+
+ public <T> T getValue(Class<T> clazz, String key) {
+ return (T) CONTEXT.get(key);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/DomElement.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/DomElement.java
new file mode 100644
index 00000000..44c538d6
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/DomElement.java
@@ -0,0 +1,242 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.Stack;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.NullLayouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Position;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.DrawNothingRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class DomElement {
+ @Getter
+ @Setter(AccessLevel.PACKAGE)
+ private Widget widget;
+ @Getter
+ @Setter(AccessLevel.PACKAGE)
+ private Renderer renderer = DrawNothingRenderer.INSTANCE; // renders element itself.
+ @Getter
+ @Setter(AccessLevel.PACKAGE)
+ private Layouter layouter = NullLayouter.INSTANCE; // layouts subelements
+
+ @Getter
+ @Setter
+ private DomElement parent;
+ @Getter
+ private List<DomElement> children = new ArrayList<>();
+
+ @Getter
+ Context context;
+
+ public DomElement() {
+ }
+
+ @Getter
+ private boolean isMounted;
+
+
+ public void setMounted(boolean mounted) {
+ if (isMounted == mounted) {
+ return;
+ }
+ isMounted = mounted;
+
+ for (DomElement child : children) {
+ child.context = context;
+ child.setMounted(mounted);
+ }
+
+ if (mounted) widget.onMount();
+ else {
+ if (isFocused())
+ context.CONTEXT.put("focus", null);
+ widget.onUnmount();
+ };
+ }
+
+ @Getter
+ private Rect relativeBound; // relative bound from parent, unapplied transformation
+
+ public void setRelativeBound(Rect relativeBound) {
+ this.relativeBound = relativeBound;
+ this.size = new Size(relativeBound.getWidth(), relativeBound.getHeight());
+ }
+
+ @Getter @Setter
+ private Size size;
+
+ @Getter @Setter
+ private Rect absBounds; // absolute bound from screen top left
+
+
+
+ // event propagation
+
+ public void requestRelayout() {
+ if (parent != null)
+ parent.requestRelayout();
+ }
+
+ public void addElementFirst(DomElement element) {
+ element.setParent(this);
+ children.add(0, element);
+ element.context = context;
+ element.setMounted(isMounted);
+ requestRelayout();
+ }
+ public void addElement(DomElement element) {
+ element.setParent(this);
+ children.add(element);
+ element.context = context;
+ element.setMounted(isMounted);
+ requestRelayout();
+ }
+ public void removeElement(DomElement element) {
+ element.setParent(null);
+ children.remove(element);
+ element.setMounted(false);
+ requestRelayout();
+ }
+
+ public void keyPressed0(char typedChar, int keyCode) {
+ for (DomElement childComponent : children) {
+ childComponent.keyPressed0(typedChar, keyCode);
+ if (widget instanceof Stack) break;
+ }
+
+ if (isFocused())
+ widget.keyPressed(typedChar, keyCode);
+ }
+ public void keyHeld0(char typedChar, int keyCode) {
+ for (DomElement childComponent : children) {
+ childComponent.keyHeld0(typedChar, keyCode);
+ if (widget instanceof Stack) break;
+ }
+
+ if (isFocused())
+ widget.keyHeld(typedChar, keyCode);
+ }
+ public void keyReleased0(char typedChar, int keyCode) {
+ for (DomElement childComponent : children) {
+ childComponent.keyReleased0(typedChar, keyCode);
+ if (widget instanceof Stack) break;
+ }
+ if (isFocused())
+ widget.keyReleased(typedChar, keyCode);
+ }
+
+ public void obtainFocus() {
+ context.CONTEXT.put("focus", this);
+ }
+
+ public boolean isFocused() {
+ return context.getValue(DomElement.class, "focus") == this;
+ }
+
+ public boolean mouseClicked0(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int mouseButton) {
+ if (absBounds == null) return false;
+ if (!absBounds.contains(absMouseX, absMouseY)) {
+ return false;
+ }
+
+ for (DomElement childComponent : children) {
+ Position transformed = renderer.transformPoint(childComponent, new Position(relMouseX0, relMouseY0));
+
+ if (childComponent.mouseClicked0(absMouseX, absMouseY, transformed.x, transformed.y, mouseButton)) {
+ return true;
+ }
+ }
+
+ return widget.mouseClicked(absMouseX, absMouseY, relMouseX0, relMouseY0, mouseButton);
+ }
+
+
+ public void mouseReleased0(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int state) {
+ if (absBounds == null) return;
+
+ for (DomElement childComponent : children) {
+ Position transformed = renderer.transformPoint(childComponent, new Position(relMouseX0, relMouseY0));
+
+ childComponent.mouseReleased0(absMouseX, absMouseY, transformed.x, transformed.y, state);
+ }
+ widget.mouseReleased(absMouseX, absMouseY, relMouseX0, relMouseY0, state);
+ }
+
+ public void mouseClickMove0(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int clickedMouseButton, long timeSinceLastClick) {
+ if (absBounds == null) return;
+
+ for (DomElement childComponent : children) {
+ Position transformed = renderer.transformPoint(childComponent, new Position(relMouseX0, relMouseY0));
+
+ childComponent.mouseClickMove0(absMouseX, absMouseY, transformed.x, transformed.y, clickedMouseButton, timeSinceLastClick);
+ }
+ if (isFocused())
+ widget.mouseClickMove(absMouseX, absMouseY, relMouseX0, relMouseY0, clickedMouseButton, timeSinceLastClick);
+ }
+ public boolean mouseScrolled0(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) {
+ if (absBounds == null) return false;
+
+ for (DomElement childComponent : children) {
+ Position transformed = renderer.transformPoint(childComponent, new Position(relMouseX0, relMouseY0));
+
+ if (childComponent.mouseScrolled0(absMouseX, absMouseY, transformed.x, transformed.y, scrollAmount)) {
+ return true;
+ }
+ }
+ if (!absBounds.contains(absMouseX, absMouseY) && !isFocused()) return false;
+ return widget.mouseScrolled(absMouseX, absMouseY, relMouseX0, relMouseY0, scrollAmount);
+ }
+
+
+ private boolean wasMouseIn = false;
+ public boolean mouseMoved0(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) {
+ if (absBounds == null) return false;
+ if (!absBounds.contains(absMouseX, absMouseY)) {
+ if (wasMouseIn) widget.mouseExited(absMouseX, absMouseY, relMouseX0, relMouseY0);
+ wasMouseIn = false;
+ return false;
+ }
+ if (!wasMouseIn) widget.mouseEntered(absMouseX, absMouseY, relMouseX0, relMouseY0);
+ wasMouseIn = true;
+
+ for (DomElement childComponent : children) {
+ Position transformed = renderer.transformPoint(childComponent, new Position(relMouseX0, relMouseY0));
+
+ if (childComponent.mouseMoved0(absMouseX, absMouseY, transformed.x, transformed.getY())) {
+ return true;
+ }
+ }
+ return widget.mouseMoved(absMouseX, absMouseY, relMouseX0, relMouseY0);
+ }
+
+ public void setCursor(EnumCursor enumCursor) {
+ parent.setCursor(enumCursor);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/ElementTreeWalkIterator.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/ElementTreeWalkIterator.java
new file mode 100644
index 00000000..c83fe4df
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/ElementTreeWalkIterator.java
@@ -0,0 +1,48 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Iterator;
+
+public class ElementTreeWalkIterator implements Iterator<DomElement>, Iterable<DomElement> {
+ private DomElement current;
+ public ElementTreeWalkIterator(DomElement child) {
+ this.current = child;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return current != null;
+ }
+
+ @Override
+ public DomElement next() {
+ DomElement c1 = current;
+ current = current.getParent();
+ return c1;
+ }
+
+ @NotNull
+ @Override
+ public Iterator<DomElement> iterator() {
+ return this;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/GuiScreenAdapter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/GuiScreenAdapter.java
new file mode 100644
index 00000000..438431e4
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/GuiScreenAdapter.java
@@ -0,0 +1,284 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.RootDom;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor;
+import kr.syeyoung.dungeonsguide.mod.utils.cursor.GLCursors;
+import lombok.Getter;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.VertexBuffer;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.io.IOException;
+
+import static org.lwjgl.opengl.GL11.GL_GREATER;
+
+public class GuiScreenAdapter extends GuiScreen {
+
+ @Getter
+ private final RootDom view;
+ private boolean isOpen = false;
+
+ public GuiScreenAdapter(Widget widget) {
+ view = new RootDom(widget);
+
+ try {
+ Mouse.setNativeCursor(GLCursors.getCursor(EnumCursor.DEFAULT));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void initGui() {
+ super.initGui();
+ Keyboard.enableRepeatEvents(true);
+ isOpen = true;
+ view.setRelativeBound(new Rect(0,0,Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight));
+ view.setAbsBounds(new Rect(0,0, Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight));
+ view.setSize(new Size(Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight));
+ view.getLayouter().layout(view, new ConstraintBox(
+ Minecraft.getMinecraft().displayWidth,
+ Minecraft.getMinecraft().displayWidth,
+ Minecraft.getMinecraft().displayHeight,
+ Minecraft.getMinecraft().displayHeight
+ ));
+ view.setMounted(true);
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ int i = Mouse.getEventX();
+ int j = this.mc.displayHeight - Mouse.getEventY();
+
+ if (view.isRelayoutRequested()) {
+ long start = System.nanoTime();
+
+ view.setRelayoutRequested(false);
+ System.out.println("relayout!");
+ view.getLayouter().layout(view, new ConstraintBox(
+ Minecraft.getMinecraft().displayWidth,
+ Minecraft.getMinecraft().displayWidth,
+ Minecraft.getMinecraft().displayHeight,
+ Minecraft.getMinecraft().displayHeight
+ ));
+ System.out.println("Relayout took "+(System.nanoTime() - start) +"ns");
+ }
+
+
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ GlStateManager.pushMatrix();
+ GlStateManager.disableDepth();
+ GlStateManager.enableBlend();
+ GlStateManager.enableAlpha();
+ GlStateManager.alphaFunc(GL_GREATER, 0);
+ GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
+ GlStateManager.color(1, 1, 1, 1);
+ GlStateManager.scale(1.0 / scaledResolution.getScaleFactor(), 1.0 / scaledResolution.getScaleFactor(), 1.0d);
+ view.getRenderer().doRender(i, j, i, j, partialTicks, new RenderingContext(), view);
+ GlStateManager.alphaFunc(GL_GREATER, 0.1f);
+ GlStateManager.popMatrix();
+ GlStateManager.enableDepth();
+ GlStateManager.disableTexture2D();
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ }
+
+ @Override
+ public void keyTyped(char typedChar, int keyCode) throws IOException {
+ try {
+ view.keyPressed0(typedChar, keyCode);
+ super.keyTyped(typedChar, keyCode);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+ public void keyHeld(int keyCode, char typedChar) throws IOException {
+ try {
+ view.keyHeld0(typedChar, keyCode);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+ public void keyReleased(int keyCode, char typedChar) throws IOException {
+ try {
+ view.keyReleased0(typedChar, keyCode);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
+ try {
+ super.mouseClicked(mouseX, mouseY, mouseButton);
+ view.mouseClicked0(mouseX, mouseY
+ , mouseX, mouseY, mouseButton);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onGuiClosed() {
+ super.onGuiClosed();
+ Keyboard.enableRepeatEvents(false);
+ isOpen = false;
+
+ try {
+ Mouse.setNativeCursor(null);
+ view.setCursor(EnumCursor.DEFAULT);
+ } catch (LWJGLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void mouseReleased(int mouseX, int mouseY, int state) {
+ try {
+ view.mouseReleased0(mouseX, mouseY
+ , mouseX, mouseY, state);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
+ try {
+ view.mouseClickMove0(mouseX, mouseY
+ , mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+ public void mouseMove(int mouseX, int mouseY) {
+ try {
+ view.mouseMoved0(mouseX, mouseY
+ , mouseX, mouseY);
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+
+ private int touchValue;
+ private int eventButton;
+ private long lastMouseEvent;
+
+
+ private int lastX, lastY;
+
+
+ @Override
+ public void handleMouseInput() throws IOException {
+ if (!isOpen) return;
+ try {
+ int i = Mouse.getEventX();
+ int j = this.mc.displayHeight - Mouse.getEventY();
+ int k = Mouse.getEventButton();
+
+ if (Mouse.getEventButtonState()) {
+ if (this.mc.gameSettings.touchscreen && this.touchValue++ > 0) {
+ return;
+ }
+
+ this.eventButton = k;
+ this.lastMouseEvent = Minecraft.getSystemTime();
+ this.mouseClicked(i, j, this.eventButton);
+ } else if (k != -1) {
+ if (this.mc.gameSettings.touchscreen && --this.touchValue > 0) {
+ return;
+ }
+
+ this.eventButton = -1;
+ this.mouseReleased(i, j, k);
+ } else if (this.eventButton != -1 && this.lastMouseEvent > 0L) {
+ long l = Minecraft.getSystemTime() - this.lastMouseEvent;
+ this.mouseClickMove(i, j, this.eventButton, l);
+ }
+ if (lastX != i || lastY != j) {
+ try {
+ EnumCursor prevCursor = view.getCurrentCursor();
+ view.setCursor(EnumCursor.DEFAULT);
+ this.mouseMove(i, j);
+ EnumCursor newCursor = view.getCurrentCursor();
+ if (prevCursor != newCursor) Mouse.setNativeCursor(GLCursors.getCursor(newCursor));
+ } catch (Throwable e) {
+ if (e.getMessage() == null || !e.getMessage().contains("hack to stop"))
+ e.printStackTrace();
+ }
+ }
+
+
+ int wheel = Mouse.getEventDWheel();
+ if (wheel != 0) {
+ view.mouseScrolled0(i, j, i, j, wheel);
+ }
+ lastX = i;
+ lastY = j;
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void handleKeyboardInput() throws IOException {
+ if (!isOpen) return;
+
+ if (Keyboard.getEventKeyState()) {
+ if (Keyboard.isRepeatEvent())
+ this.keyHeld(Keyboard.getEventKey(), Keyboard.getEventCharacter());
+ else
+ this.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+ } else {
+ this.keyReleased(Keyboard.getEventKey(), Keyboard.getEventCharacter());
+ }
+
+ this.mc.dispatchKeypresses();
+ }
+
+ @Override
+ public void handleInput() throws IOException {
+// Keyboard.enableRepeatEvents(true); // i hope it's temproary solution
+ super.handleInput();
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/RootDom.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/RootDom.java
new file mode 100644
index 00000000..a9501212
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/RootDom.java
@@ -0,0 +1,74 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.Context;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.SingleChildPassingLayouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.SingleChildRenderer;
+import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+public class RootDom extends DomElement {
+
+ private static class DummyWidget extends Widget {
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return null;
+ }
+ }
+ public RootDom(Widget widget) {
+ context = new Context();
+ setLayouter(SingleChildPassingLayouter.INSTANCE);
+ setRenderer(SingleChildRenderer.INSTANCE);
+ setWidget(new DummyWidget());
+
+ DomElement element = widget.createDomElement(this);// and it's mounted!
+ addElement(element);
+ }
+
+ @Getter
+ private EnumCursor currentCursor;
+
+ @Override
+ public void setCursor(EnumCursor enumCursor) {
+ currentCursor = enumCursor;
+ }
+
+ @Getter
+ @Setter
+ private boolean relayoutRequested;
+
+ @Override
+ public void requestRelayout() {
+ relayoutRequested = true;
+ }
+
+
+
+ @Override
+ public boolean mouseClicked0(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int mouseButton) {
+ getContext().CONTEXT.put("focus", null);
+ return super.mouseClicked0(absMouseX, absMouseY, relMouseX0, relMouseY0, mouseButton);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Widget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Widget.java
new file mode 100644
index 00000000..4895ae8b
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/Widget.java
@@ -0,0 +1,94 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.SingleChildPassingLayouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.SingleChildRenderer;
+import lombok.Getter;
+
+import java.util.*;
+
+public abstract class Widget {
+
+ @Getter
+ private DomElement domElement = new DomElement();
+
+
+ private boolean multiatfirst = false;
+ public DomElement createDomElement(DomElement parent) {
+ if (domElement.getWidget() != null) throw new IllegalStateException("Controller already has corresponding DomElement!");
+ domElement = new DomElement();
+ domElement.setWidget(this);
+ domElement.setParent(parent);
+
+ domElement.setLayouter(createLayouter());
+ domElement.setRenderer(createRenderer());
+
+ // build
+ List<Widget> widgets = build(domElement);
+ for (Widget widget : widgets) {
+ DomElement domElement1 = widget.createDomElement(domElement);
+ domElement.addElement(domElement1);
+ }
+ return domElement;
+ }
+
+ protected Layouter createLayouter() {
+ if (this instanceof Layouter) return (Layouter) this;
+ return SingleChildPassingLayouter.INSTANCE;
+ }
+ protected Renderer createRenderer() {
+ if (this instanceof Renderer) return (Renderer) this;
+ return SingleChildRenderer.INSTANCE;
+ }
+
+ public abstract List<Widget> build(DomElement buildContext);
+
+ public Widget() {
+ // parameters shall be passed through constructor
+ // or maybe a "builder"
+ // set bindable props in the builder and yeah
+ // or maybe set raw props
+ }
+
+
+ public boolean mouseScrolled(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) {
+ return false;
+ }
+ public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) {
+ return false;
+ }
+ public void mouseClickMove(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int clickedMouseButton, long timeSinceLastClick) {}
+ public void mouseReleased(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int state) {}
+ public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) {
+ return false;
+ }
+ public void keyReleased(char typedChar, int keyCode) {}
+ public void keyHeld(char typedChar, int keyCode) {}
+ public void keyPressed(char typedChar, int keyCode) {}
+ public void mouseExited(int absMouseX, int absMouseY, double relMouseX, double relMouseY) {}
+ public void mouseEntered(int absMouseX, int absMouseY, double relMouseX, double relMouseY) {}
+
+ public void onMount() {
+ }
+ public void onUnmount() {
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AbsXY.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AbsXY.java
new file mode 100644
index 00000000..eb9b07da
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AbsXY.java
@@ -0,0 +1,69 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class AbsXY extends AnnotatedExportOnlyWidget implements Layouter {
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+
+ @Export(attributeName = "x")
+ public final BindableAttribute<Double> x = new BindableAttribute<>(Double.class, 0.0);
+
+ @Export(attributeName = "y")
+ public final BindableAttribute<Double> y = new BindableAttribute<>(Double.class, 0.0);
+
+ public AbsXY() {
+ x.addOnUpdate(this::setLocations);
+ y.addOnUpdate(this::setLocations);
+ }
+
+ private void setLocations(double old, double neu) {
+ if (getDomElement().getChildren().size() <= 0) return;
+ DomElement child = getDomElement().getChildren().get(0);
+ child.setRelativeBound(new Rect(x.getValue(), y.getValue(), child.getSize().getWidth(), child.getSize().getHeight()));
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ DomElement child = buildContext.getChildren().get(0);
+ Size size = child.getLayouter().layout(buildContext, new ConstraintBox(
+ 0,constraintBox.getMaxWidth() - x.getValue(), 0, constraintBox.getMaxHeight()-y.getValue()
+ ));
+ child.setRelativeBound(new Rect(x.getValue(), y.getValue(), size.getWidth(), size.getHeight()));
+ return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(child.getValue());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Align.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Align.java
new file mode 100644
index 00000000..5c1bce11
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Align.java
@@ -0,0 +1,64 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Align extends AnnotatedExportOnlyWidget implements Layouter {
+ @Export(attributeName = "hAlign")
+ public final BindableAttribute<Alignment> hAlign = new BindableAttribute<>(Alignment.class, Alignment.CENTER);
+ @Export(attributeName = "vAlign")
+ public final BindableAttribute<Alignment> vAlign = new BindableAttribute<>(Alignment.class, Alignment.CENTER);
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+ public static enum Alignment {
+ START, CENTER, END
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(child.getValue());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ DomElement theOnly = getDomElement().getChildren().get(0);
+ Size size = theOnly.getLayouter().layout(theOnly, new ConstraintBox(
+ 0, constraintBox.getMaxWidth(), 0, constraintBox.getMaxHeight()
+ ));
+ theOnly.setRelativeBound(new Rect(
+ (constraintBox.getMaxWidth() - size.getWidth())/2,
+ (constraintBox.getMaxHeight() - size.getHeight())/2,
+ size.getWidth(), size.getHeight()
+ ));
+ return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AspectRatioFitter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AspectRatioFitter.java
new file mode 100644
index 00000000..0e6f9ed9
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/AspectRatioFitter.java
@@ -0,0 +1,87 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class AspectRatioFitter extends AnnotatedExportOnlyWidget implements Layouter {
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "width")
+ public final BindableAttribute<Integer> width = new BindableAttribute<>(Integer.class, 1);
+ @Export(attributeName = "height")
+ public final BindableAttribute<Integer> height = new BindableAttribute<>(Integer.class, 1);
+
+ @Export(attributeName = "fit")
+ public final BindableAttribute<Flexible.FlexFit> fit = new BindableAttribute<>(Flexible.FlexFit.class, Flexible.FlexFit.TIGHT);
+
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ // ratio is width/height
+
+
+ double heightIfWidthFit = constraintBox.getMaxWidth() * height.getValue() / width.getValue();
+ double widthIfHeightFit = constraintBox.getMaxHeight() * width.getValue() / height.getValue();
+
+ DomElement target = null;
+ if (!buildContext.getChildren().isEmpty())
+ target = buildContext.getChildren().get(0);
+ if (heightIfWidthFit <= constraintBox.getMaxHeight()) {
+ if (target != null) {
+ Size size = target.getLayouter().layout(target, new ConstraintBox(
+ fit.getValue() == Flexible.FlexFit.LOOSE ? constraintBox.getMinWidth() : constraintBox.getMaxWidth(), constraintBox.getMaxWidth(),
+ fit.getValue() == Flexible.FlexFit.LOOSE ? constraintBox.getMinHeight() : heightIfWidthFit, heightIfWidthFit
+ ));
+ target.setRelativeBound(new Rect(0,0,size.getWidth(), size.getHeight()));
+ return size;
+ }
+ return new Size(constraintBox.getMaxWidth(), heightIfWidthFit);
+ } else if (widthIfHeightFit <= constraintBox.getMaxWidth()){
+ if (target != null) {
+ Size size = target.getLayouter().layout(target, new ConstraintBox(
+ fit.getValue() == Flexible.FlexFit.LOOSE ? constraintBox.getMinWidth() : widthIfHeightFit, widthIfHeightFit,
+ fit.getValue() == Flexible.FlexFit.LOOSE ? constraintBox.getMinHeight() : constraintBox.getMaxHeight(), constraintBox.getMaxHeight()
+ ));
+ target.setRelativeBound(new Rect(0,0,size.getWidth(), size.getHeight()));
+ return size;
+ }
+
+ return new Size(widthIfHeightFit, constraintBox.getMaxHeight());
+ } else {
+ throw new IllegalStateException("How is this possible mathmatically?");
+ }
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(widget.getValue());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Background.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Background.java
new file mode 100644
index 00000000..e808f2fd
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Background.java
@@ -0,0 +1,61 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.SingleChildRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+// passes down constraints
+// but sets background!! cool!!!
+public class Background extends AnnotatedExportOnlyWidget {
+
+ @Export(attributeName = "backgroundColor")
+ public final BindableAttribute<Integer> color = new BindableAttribute<>(Integer.class, 0xFFFFFFFF);
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return child.getValue() == null ? Collections.EMPTY_LIST : Collections.singletonList(child.getValue());
+ }
+
+ @Override
+ protected Renderer createRenderer() {
+ return new BRender();
+ }
+
+ public class BRender extends SingleChildRenderer {
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext renderingContext, DomElement buildContext) {
+ renderingContext.drawRect(0,0,buildContext.getSize().getWidth(),buildContext.getSize().getHeight(),
+ color.getValue()
+ );
+ super.doRender(absMouseX, absMouseY, relMouseX, relMouseY, partialTicks, renderingContext, buildContext);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Border.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Border.java
new file mode 100644
index 00000000..e743a8af
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Border.java
@@ -0,0 +1,140 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.OnlyChildrenRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import scala.tools.nsc.doc.base.comment.Link;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Border extends AnnotatedExportOnlyWidget implements Layouter {
+ @Export(attributeName = "$left")
+ public final BindableAttribute<Widget> left = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "$right")
+ public final BindableAttribute<Widget> right = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "$top")
+ public final BindableAttribute<Widget> top = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "$bottom")
+ public final BindableAttribute<Widget> bottom = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "$content")
+ public final BindableAttribute<Widget> content = new BindableAttribute<>(Widget.class);
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ // layout borders, ask them about their constraints
+ // then layout content with space less than blahblah
+ // then relayout borders.
+ DomElement top = null, bottom = null, left = null, right = null, content = null;
+ for (DomElement child : buildContext.getChildren()) {
+ if (child.getWidget() == this.top.getValue()) top = child;
+ if (child.getWidget() == this.bottom.getValue()) bottom = child;
+ if (child.getWidget() == this.left.getValue()) left = child;
+ if (child.getWidget() == this.right.getValue()) right = child;
+ if (child.getWidget() == this.content.getValue()) content = child;
+ }
+
+
+
+ double th = 0, bh = 0;
+ {
+ if (top != null)
+ th= top.getLayouter().layout(top, new ConstraintBox(constraintBox.getMaxWidth(), constraintBox.getMaxWidth(), 0, constraintBox.getMaxHeight())).getHeight();
+ if (bottom != null)
+ bh = bottom.getLayouter().layout(bottom, new ConstraintBox(constraintBox.getMaxWidth(), constraintBox.getMaxWidth(), 0, constraintBox.getMaxHeight())).getHeight();
+ }
+
+ double lw = 0, rw = 0;
+ {
+ if (left != null)
+ lw= left.getLayouter().layout(left, new ConstraintBox(0, constraintBox.getMaxWidth(), constraintBox.getMaxHeight(), constraintBox.getMaxHeight())).getWidth();
+ if (right != null)
+ rw= right.getLayouter().layout(right, new ConstraintBox(0, constraintBox.getMaxWidth(), constraintBox.getMaxHeight(), constraintBox.getMaxHeight())).getWidth();
+ }
+
+ Size dimension = content.getLayouter().layout(content, new ConstraintBox(
+ 0, constraintBox.getMaxWidth() - lw - rw,
+ 0, constraintBox.getMaxHeight() - th - bh
+ ));
+
+
+ {
+ if (left != null)
+ lw= left.getLayouter().layout(left, new ConstraintBox(lw, lw, dimension.getHeight(), dimension.getHeight())).getWidth();
+ if (right != null)
+ rw= right.getLayouter().layout(right, new ConstraintBox(rw, rw, dimension.getHeight(), dimension.getHeight())).getWidth();
+ }
+ {
+ if (top != null)
+ th= top.getLayouter().layout(top, new ConstraintBox(dimension.getWidth() + lw + rw, dimension.getWidth() + lw + rw, th, th)).getHeight();
+ if (bottom != null)
+ bh = bottom.getLayouter().layout(buildContext, new ConstraintBox(dimension.getWidth() + lw + rw, dimension.getWidth() + lw + rw, bh,bh)).getHeight();
+ }
+
+ if (top != null)
+ top.setRelativeBound(new Rect(0,0, dimension.getWidth() + lw + rw, th));
+ if (bottom != null)
+ bottom.setRelativeBound(new Rect(0, dimension.getHeight() + th, dimension.getWidth() + lw + rw, bh));
+ if (left != null)
+ left.setRelativeBound(new Rect(0, th, lw, dimension.getHeight()));
+ if (right != null)
+ right.setRelativeBound(new Rect(lw + dimension.getWidth(), th, rw, dimension.getHeight()));
+
+ content.setRelativeBound(new Rect(
+ th, lw, dimension.getWidth(), dimension.getHeight()
+ ));
+
+
+
+ return new Size(dimension.getWidth() + lw + rw, dimension.getHeight() + th + bh);
+ }
+
+ @Override
+ protected Renderer createRenderer() {
+ return OnlyChildrenRenderer.INSTANCE;
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ List<Widget> widgets = new LinkedList<>();
+ widgets.add(content.getValue());
+ if (top.getValue() != null)
+ widgets.add(top.getValue());
+ if (bottom.getValue() != null)
+ widgets.add(bottom.getValue());
+ if (left.getValue() != null)
+ widgets.add(left.getValue());
+ if (right.getValue() != null)
+ widgets.add(right.getValue());
+ return widgets;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Button.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Button.java
new file mode 100644
index 00000000..d020967e
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Button.java
@@ -0,0 +1,127 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Passthrough;
+import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+
+@Passthrough(exportName = "$", bindName = "wgtNormal", type = Widget.class)
+@Passthrough(exportName = "$hovered", bindName = "wgtHover", type = Widget.class)
+@Passthrough(exportName = "$pressed", bindName = "wgtPressed", type = Widget.class)
+@Passthrough(exportName = "$disabled", bindName = "wgtDisabled", type = Widget.class)
+public class Button extends AnnotatedWidget implements Renderer {
+
+ @Bind(variableName = "refDisabled")
+ public final BindableAttribute<DomElement> disabled = new BindableAttribute<DomElement>(DomElement.class);
+ @Bind(variableName = "refPressed")
+ public final BindableAttribute<DomElement> pressed = new BindableAttribute<DomElement>(DomElement.class);
+ @Bind(variableName = "refHover")
+ public final BindableAttribute<DomElement> hover = new BindableAttribute<DomElement>(DomElement.class);
+ @Bind(variableName = "refNormal")
+ public final BindableAttribute<DomElement> normal = new BindableAttribute<DomElement>(DomElement.class);
+
+
+ @Export(attributeName = "click")
+ public final BindableAttribute<Runnable> onClick = new BindableAttribute<>(Runnable.class);
+ @Export(attributeName = "disabled")
+ public final BindableAttribute<Boolean> isDisabled = new BindableAttribute<>(Boolean.class);
+
+ public Button() {
+ super(new ResourceLocation("dungeonsguide:gui/elements/button.gui"));
+ }
+
+ @Override
+ protected Layouter createLayouter() {
+ return Stack.StackingLayouter.INSTANCE;
+ }
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ boolean isHover = buildContext.getSize().contains(relMouseX, relMouseY);
+ DomElement value;
+ if (isDisabled.getValue()) {
+ value = disabled.getValue();
+ } else if (isPressed) {
+ value = pressed.getValue();
+ } else if (isHover) {
+ value = hover.getValue();
+ } else {
+ value = normal.getValue();
+ }
+ Rect original = value.getRelativeBound();
+ GlStateManager.translate(original.getX(), original.getY(), 0);
+
+ double absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth();
+ double absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight();
+
+ Rect elementABSBound = new Rect(
+ (buildContext.getAbsBounds().getX() + original.getX() * absXScale),
+ (buildContext.getAbsBounds().getY() + original.getY() * absYScale),
+ (original.getWidth() * absXScale),
+ (original.getHeight() * absYScale)
+ );
+ value.setAbsBounds(elementABSBound);
+
+ value.getRenderer().doRender(absMouseX, absMouseY,
+ relMouseX - original.getX(),
+ relMouseY - original.getY(), partialTicks, context, value);
+ }
+
+ private boolean isPressed;
+ @Override
+ public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) {
+ getDomElement().obtainFocus();
+ isPressed = true;
+ return onClick.getValue() != null;
+ }
+
+ @Override
+ public void mouseReleased(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int state) {
+ if (!isPressed) return;
+ isPressed = false;
+
+
+ if (isDisabled.getValue()) return;
+ if (getDomElement().getAbsBounds().contains(absMouseX, absMouseY)) {
+ if (onClick.getValue() != null) onClick.getValue().run();
+ }
+ super.mouseReleased(absMouseX, absMouseY, relMouseX, relMouseY, state);
+ }
+
+ @Override
+ public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) {
+ if (isDisabled.getValue())
+ getDomElement().setCursor(EnumCursor.NOT_ALLOWED);
+ else
+ getDomElement().setCursor(EnumCursor.POINTING_HAND);
+ return true;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Clip.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Clip.java
new file mode 100644
index 00000000..1eb52bf4
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Clip.java
@@ -0,0 +1,70 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Clip extends AnnotatedExportOnlyWidget implements Renderer {
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ if (buildContext.getChildren().isEmpty()) return;
+ context.pushClip(buildContext.getAbsBounds(), buildContext.getSize(), 0,0, buildContext.getSize().getWidth(), buildContext.getSize().getHeight());
+
+ DomElement value = buildContext.getChildren().get(0);
+
+ Rect original = value.getRelativeBound();
+ GlStateManager.translate(original.getX(), original.getY(), 0);
+
+ double absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth();
+ double absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight();
+
+ Rect elementABSBound = new Rect(
+ (buildContext.getAbsBounds().getX() + original.getX() * absXScale),
+ (buildContext.getAbsBounds().getY() + original.getY() * absYScale),
+ (original.getWidth() * absXScale),
+ (original.getHeight() * absYScale)
+ );
+ value.setAbsBounds(elementABSBound);
+
+ value.getRenderer().doRender(absMouseX, absMouseY,
+ relMouseX - original.getX(),
+ relMouseY - original.getY(), partialTicks, context, value);
+ context.popClip();
+ }
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class);
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(widget.getValue());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Column.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Column.java
new file mode 100644
index 00000000..f61838ee
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Column.java
@@ -0,0 +1,193 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.OnlyChildrenRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.WidgetList;
+import org.apache.logging.log4j.core.Layout;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Column extends AnnotatedExportOnlyWidget implements Layouter {
+ public static enum CrossAxisAlignment {
+ START, CENTER, END, STRETCH
+ }
+ public static enum MainAxisAlignment {
+ START, CENTER, END, SPACE_EVENLY, SPACE_AROUND, SPACE_BETWEEN
+ }
+
+ @Export(attributeName = "crossAlign")
+ public final BindableAttribute<CrossAxisAlignment> hAlign = new BindableAttribute<>(CrossAxisAlignment.class, CrossAxisAlignment.CENTER);
+
+ @Export(attributeName = "mainAlign")
+ public final BindableAttribute<MainAxisAlignment> vAlign = new BindableAttribute<>(MainAxisAlignment.class, MainAxisAlignment.START);
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<WidgetList> widgets = new BindableAttribute<>(WidgetList.class);
+
+ @Export(attributeName = "api")
+ public final BindableAttribute<Column> api = new BindableAttribute<>(Column.class, this);
+
+ public Column() {
+ hAlign.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ vAlign.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return widgets.getValue();
+ }
+
+ @Override
+ protected Renderer createRenderer() {
+ return OnlyChildrenRenderer.INSTANCE;
+ }
+
+ public void addWidget(Widget widget) {
+ if (getDomElement().getWidget() == null) {
+ widgets.getValue().add(widget);
+ } else {
+ DomElement domElement = widget.createDomElement(getDomElement());
+ getDomElement().addElement(domElement);
+ }
+ }
+
+ public void removeWidget(Widget widget) {
+ getDomElement().removeElement(widget.getDomElement());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraints) {
+ double width = 0;
+ double height = 0;
+ double effheight = constraints.getMaxHeight(); // max does not count for column.
+ CrossAxisAlignment crossAxisAlignment = hAlign.getValue();
+ MainAxisAlignment mainAxisAlignment = vAlign.getValue();
+ Map<DomElement, Size> saved = new HashMap<>();
+ for (DomElement child : getDomElement().getChildren()) {
+ if (!(child.getWidget() instanceof Flexible)) {
+ Size requiredSize = child.getLayouter().layout(child, new ConstraintBox(
+ crossAxisAlignment == CrossAxisAlignment.STRETCH
+ ? constraints.getMaxWidth() : 0, constraints.getMaxWidth(), 0, Integer.MAX_VALUE
+ ));
+ saved.put(child, requiredSize);
+ width = Math.max(width, requiredSize.getWidth());
+ height += requiredSize.getHeight();
+ }
+ }
+
+
+
+ boolean flexFound = false;
+ int sumFlex = 0;
+ for (DomElement child : getDomElement().getChildren()) {
+ if (child.getWidget() instanceof Flexible) {
+ sumFlex += Math.min(1, ((Flexible) child.getWidget()).flex.getValue());
+ flexFound = true;
+ }
+ }
+
+ if (flexFound && effheight == Integer.MAX_VALUE) throw new IllegalStateException("Max height can not be infinite with flex elements");
+ else if (effheight == Integer.MAX_VALUE) effheight = height;
+
+ if (flexFound) {
+ double remainingHeight = effheight - height;
+ double heightPer = remainingHeight / sumFlex;
+
+ for (DomElement child : getDomElement().getChildren()) {
+ if (child.getWidget() instanceof Flexible) {
+ Size requiredSize = child.getLayouter().layout(child, new ConstraintBox(
+ crossAxisAlignment == CrossAxisAlignment.STRETCH
+ ? constraints.getMaxWidth() : 0, constraints.getMaxWidth(), 0, heightPer * ((Flexible) child.getWidget()).flex.getValue()
+ ));
+ saved.put(child, requiredSize);
+ width = Math.max(width, requiredSize.getWidth());
+ height += requiredSize.getHeight();
+ }
+ }
+ }
+ width = constraints.getMaxWidth() == Integer.MAX_VALUE ? width : constraints.getMaxWidth();
+
+
+
+ double starty = 0;
+ double heightDelta = 0;
+
+ if (mainAxisAlignment == MainAxisAlignment.CENTER)
+ starty = (effheight - height) / 2;
+ else if (mainAxisAlignment == MainAxisAlignment.END)
+ starty = effheight - height;
+ else if (mainAxisAlignment == MainAxisAlignment.SPACE_BETWEEN) {
+ double remaining = effheight - height;
+ if (remaining > 0) {
+ starty = 0;
+ heightDelta = remaining / (getDomElement().getChildren().size()-1);
+ } else {
+ starty = (effheight - height) / 2;
+ }
+ } else if (mainAxisAlignment == MainAxisAlignment.SPACE_EVENLY) {
+ double remaining = effheight - height;
+ if (remaining > 0) {
+ heightDelta = remaining / (getDomElement().getChildren().size()+1);
+ starty = heightDelta;
+ } else {
+ starty= (effheight - height) / 2;
+ }
+ } else if (mainAxisAlignment == MainAxisAlignment.SPACE_AROUND) {
+ double remaining = effheight - height;
+ if (remaining > 0) {
+ heightDelta = 2 * remaining / getDomElement().getChildren().size();
+ starty = heightDelta / 2;
+ } else {
+ starty = (effheight - height / 2);
+ }
+ }
+
+ for (DomElement child : getDomElement().getChildren()) {
+ Size size = saved.get(child);
+
+ child.setRelativeBound(new Rect(
+ crossAxisAlignment == CrossAxisAlignment.START ? 0 :
+ crossAxisAlignment == CrossAxisAlignment.CENTER ? (width-size.getWidth())/2 :
+ crossAxisAlignment == CrossAxisAlignment.STRETCH ? (width - size.getWidth()) /2 :
+ width - size.getWidth(), starty
+ ,size.getWidth(), size.getHeight()
+ ));
+ starty += size.getHeight();
+ starty += heightDelta;
+ }
+ return new Size(
+ width,
+ effheight
+ );
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Flexible.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Flexible.java
new file mode 100644
index 00000000..b799cb3b
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Flexible.java
@@ -0,0 +1,77 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.SingleChildRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.WidgetList;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.List;
+
+// yes it's that flexible from flutter
+public class Flexible extends AnnotatedExportOnlyWidget implements Layouter {
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class);
+
+ @Export(attributeName = "flex")
+ public final BindableAttribute<Integer> flex = new BindableAttribute<>(Integer.class, 1);
+
+ @Export(attributeName = "fit")
+ public final BindableAttribute<FlexFit> fit = new BindableAttribute<>(FlexFit.class, FlexFit.TIGHT);
+
+ public static enum FlexFit {
+ TIGHT, LOOSE
+ }
+
+ public Flexible() {
+ flex.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ fit.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(widget.getValue());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ FlexFit fit = this.fit.getValue();
+ ConstraintBox box =
+ fit == FlexFit.TIGHT ?
+ new ConstraintBox(constraintBox.getMaxWidth() == Integer.MAX_VALUE ? 0 : constraintBox.getMaxWidth(), constraintBox.getMaxWidth()
+ , constraintBox.getMaxHeight() == Integer.MAX_VALUE ? 0 : constraintBox.getMaxHeight(), constraintBox.getMaxHeight())
+ : ConstraintBox.loose(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ DomElement child = getDomElement().getChildren().get(0);
+
+ Size dim = child.getLayouter().layout(child, box);
+
+ getDomElement().getChildren().get(0).setRelativeBound(new Rect(0,0, dim.getWidth(),dim.getHeight()));
+ return dim;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Line.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Line.java
new file mode 100644
index 00000000..d84cfa46
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Line.java
@@ -0,0 +1,122 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.opengl.GL11;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Line extends AnnotatedExportOnlyWidget implements Layouter, Renderer{
+ @Export(attributeName = "thickness")
+ public final BindableAttribute<Float> thickness =new BindableAttribute<>(Float.class, 1.0f);
+
+ @Export(attributeName = "factor")
+ public final BindableAttribute<Integer> factor =new BindableAttribute<>(Integer.class, 1); // normal line
+
+ @Export(attributeName = "pattern")
+ public final BindableAttribute<Short> pattern =new BindableAttribute<>(Short.class, null); // normal line
+
+ @Export(attributeName = "color")
+ public final BindableAttribute<Integer> color = new BindableAttribute<>(Integer.class, 0xFF000000);
+ @Export(attributeName = "dir")
+ public final BindableAttribute<Orientation> direction = new BindableAttribute<>(Orientation.class, Orientation.HORIZONTAL);
+
+
+ public float r = 0;
+ public float g = 0;
+ public float b = 0;
+ public float a = 1;
+
+
+ public static enum Orientation {
+ VERTICAL, HORIZONTAL
+ }
+
+ public Line() {
+ thickness.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ color.addOnUpdate((old, color) -> {
+ a = ((color >> 24) & 0xFF) / 255.0f;
+ r = ((color >> 16) &0xFF) /255.0f;
+ g = ((color >> 8) & 0xFF) / 255.0f;
+ b = (color & 0xFF) / 255.0f;
+ });
+ }
+
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ double thickness = this.thickness.getValue();
+ Orientation orientation = direction.getValue();
+ if (orientation == Orientation.HORIZONTAL) {
+ return new Size(
+ constraintBox.getMaxWidth(),
+ Layouter.clamp( thickness, constraintBox.getMinHeight(), constraintBox.getMaxHeight())
+ );
+ } else {
+ return new Size(
+ Layouter.clamp( thickness, constraintBox.getMinWidth(), constraintBox.getMaxWidth()),
+ constraintBox.getMaxHeight()
+ );
+ }
+ }
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ double w = buildContext.getSize().getWidth(), h = buildContext.getSize().getHeight();
+
+ GlStateManager.color(r,g,b,a);
+ GlStateManager.disableTexture2D();
+ GL11.glLineWidth(thickness.getValue());
+
+ Short pattern = this.pattern.getValue();
+ if (pattern != null) {
+ GL11.glLineStipple(factor.getValue(), pattern);
+ GL11.glEnable(GL11.GL_LINE_STIPPLE);
+ }
+
+ GL11.glBegin(GL11.GL_LINES);
+ if (direction.getValue() == Orientation.HORIZONTAL) {
+ GL11.glVertex2d(0,h/2.0f);
+ GL11.glVertex2d(w, h/2.0f);
+ } else {
+ GL11.glVertex2d(w/2.0f,0);
+ GL11.glVertex2d(w/2.0f, h);
+ }
+ GL11.glEnd();
+
+ if (pattern != null) {
+ GL11.glDisable(GL11.GL_LINE_STIPPLE);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Measure.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Measure.java
new file mode 100644
index 00000000..24da1a93
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Measure.java
@@ -0,0 +1,59 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Measure extends AnnotatedExportOnlyWidget implements Layouter {
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ if (buildContext.getChildren().isEmpty()) {
+ return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ }
+
+ DomElement childCtx = buildContext.getChildren().get(0);
+
+ Size dim = childCtx.getLayouter().layout(childCtx, constraintBox);
+ childCtx.setRelativeBound(new Rect(0,0, dim.getWidth(), dim.getHeight()));
+ size.setValue(dim);
+ return new Size(dim.getWidth(), dim.getHeight());
+ }
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class);
+
+ @Export(attributeName = "size")
+ public final BindableAttribute<Size> size = new BindableAttribute<>(Size.class);
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(widget.getValue());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Padding.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Padding.java
new file mode 100644
index 00000000..396efbe0
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Padding.java
@@ -0,0 +1,80 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Padding extends AnnotatedExportOnlyWidget implements Layouter {
+
+ @Export(attributeName = "left")
+ public final BindableAttribute<Double> left = new BindableAttribute<>(Double.class, 0.0);
+ @Export(attributeName = "right")
+ public final BindableAttribute<Double> right = new BindableAttribute<>(Double.class, 0.0);
+ @Export(attributeName = "top")
+ public final BindableAttribute<Double> top = new BindableAttribute<>(Double.class, 0.0);
+ @Export(attributeName = "bottom")
+ public final BindableAttribute<Double> bottom = new BindableAttribute<>(Double.class, 0.0);
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+
+ public Padding() {
+ left.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ right.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ top.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ bottom.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(child.getValue());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ DomElement domElement = getDomElement().getChildren().get(0);
+
+ double width = (left.getValue() + right.getValue());
+ double height = (top.getValue() + bottom.getValue());
+ Size dim = domElement.getLayouter().layout(domElement, new ConstraintBox(
+ constraintBox.getMinWidth() - width,
+ constraintBox.getMaxWidth() - width,
+ constraintBox.getMinHeight() - height,
+ constraintBox.getMaxHeight() - height
+ ));
+
+ domElement.setRelativeBound(new Rect(
+ left.getValue().intValue(),
+ top.getValue().intValue(),
+ dim.getWidth(),
+ dim.getHeight()
+ ));
+
+
+ return new Size(dim.getWidth() + width, dim.getHeight() + height);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Placeholder.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Placeholder.java
new file mode 100644
index 00000000..c28ae94e
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Placeholder.java
@@ -0,0 +1,66 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.NullLayouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.opengl.GL11;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Placeholder extends AnnotatedExportOnlyWidget implements Renderer {
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ double w = buildContext.getSize().getWidth(), h = buildContext.getSize().getHeight();
+ context.drawRect(0,0,w,h, 0xFFFFFFFF);
+ GlStateManager.color(0,0,0,1);
+ GlStateManager.disableTexture2D();
+ GL11.glLineWidth(1.0f);
+ GL11.glBegin(GL11.GL_LINE_LOOP);
+ GL11.glVertex2d(0,0);
+ GL11.glVertex2d(w, 0);
+ GL11.glVertex2d(w, h);
+ GL11.glVertex2d(0, h);
+ GL11.glEnd();
+ GL11.glBegin(GL11.GL_LINES);
+ GL11.glVertex2d(0,0);
+ GL11.glVertex2d(w,h);
+ GL11.glVertex2d(w,0);
+ GL11.glVertex2d(0, h);
+ GL11.glEnd();
+ }
+
+ @Override
+ protected Layouter createLayouter() {
+ return NullLayouter.INSTANCE;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/PopupMgr.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/PopupMgr.java
new file mode 100644
index 00000000..d87b588e
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/PopupMgr.java
@@ -0,0 +1,68 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import net.minecraft.util.ResourceLocation;
+
+public class PopupMgr extends AnnotatedWidget {
+ public PopupMgr() {
+ super(new ResourceLocation("dungeonsguide:gui/elements/popupmgr.gui"));
+ }
+ // just stack
+
+ @Bind(variableName = "stackRef")
+ public final BindableAttribute<DomElement> domElementBindableAttribute = new BindableAttribute<>(DomElement.class);
+
+
+ @Export(attributeName = "$")
+ @Bind(variableName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+
+
+ @Override
+ public void onMount() {
+ super.onMount();
+ getDomElement().getContext().CONTEXT.put("popup", this);
+ }
+
+ @Override
+ public void onUnmount() {
+ domElementBindableAttribute.getValue().getChildren().clear();
+ super.onUnmount();
+ }
+
+ public static PopupMgr getPopupMgr(DomElement buildContext) {
+ return buildContext.getContext().getValue(PopupMgr.class, "popup");
+ }
+
+ public void openPopup(Widget element) {
+ domElementBindableAttribute.getValue().addElementFirst(element.createDomElement(getDomElement()));
+ }
+
+ public void closePopup() {
+ domElementBindableAttribute.getValue().removeElement(
+ domElementBindableAttribute.getValue().getChildren().get(0)
+ );
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Row.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Row.java
new file mode 100644
index 00000000..5a159e1b
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Row.java
@@ -0,0 +1,196 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.OnlyChildrenRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.WidgetList;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Row extends AnnotatedExportOnlyWidget implements Layouter {
+ public static enum CrossAxisAlignment {
+ START, CENTER, END, STRETCH
+ }
+ public static enum MainAxisAlignment {
+ START, CENTER, END, SPACE_EVENLY, SPACE_AROUND, SPACE_BETWEEN
+ }
+
+ @Export(attributeName = "crossAlign")
+ public final BindableAttribute<CrossAxisAlignment> vAlign = new BindableAttribute<>(CrossAxisAlignment.class, CrossAxisAlignment.CENTER);
+
+ @Export(attributeName = "mainAlign")
+ public final BindableAttribute<MainAxisAlignment> hAlign = new BindableAttribute<>(MainAxisAlignment.class, MainAxisAlignment.START);
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<WidgetList> children = new BindableAttribute<>(WidgetList.class);
+
+
+ @Export(attributeName = "api")
+ public final BindableAttribute<Row> rowAPI = new BindableAttribute<>(Row.class, this);
+ public Row() {
+ hAlign.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ vAlign.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return children.getValue();
+ }
+
+ @Override
+ protected Renderer createRenderer() {
+ return OnlyChildrenRenderer.INSTANCE;
+ }
+
+
+ public void addWidget(Widget widget) {
+ if (getDomElement().getWidget() == null) {
+ children.getValue().add(widget);
+ } else {
+ DomElement domElement = widget.createDomElement(getDomElement());
+ getDomElement().addElement(domElement);
+ }
+ }
+
+ public void removeWidget(Widget widget) {
+ getDomElement().removeElement(widget.getDomElement());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ double height = 0;
+ double width = 0;
+ double effwidth = constraintBox.getMaxWidth(); // max does not count for row.
+
+ CrossAxisAlignment crossAxisAlignment = vAlign.getValue();
+ MainAxisAlignment mainAxisAlignment = hAlign.getValue();
+ Map<DomElement, Size> saved = new HashMap<>();
+
+ for (DomElement child : getDomElement().getChildren()) {
+ if (!(child.getWidget() instanceof Flexible)) {
+ Size requiredSize = child.getLayouter().layout(child, new ConstraintBox(
+ 0, Integer.MAX_VALUE,
+ crossAxisAlignment == CrossAxisAlignment.STRETCH ? constraintBox.getMaxHeight() : 0, constraintBox.getMaxHeight()
+ ));
+ saved.put(child, requiredSize);
+ height = Math.max(height, requiredSize.getHeight());
+ width += requiredSize.getWidth();
+ }
+ }
+
+
+ boolean flexFound = false;
+ int sumFlex = 0;
+ for (DomElement child : getDomElement().getChildren()) {
+ if (child.getWidget() instanceof Flexible) {
+ sumFlex += Math.min(1, ((Flexible) child.getWidget()).flex.getValue());
+ flexFound =true;
+ }
+ }
+
+ if (flexFound && effwidth == Integer.MAX_VALUE) throw new IllegalStateException("Max width can not be infinite with flex elements");
+ else if (effwidth == Integer.MAX_VALUE) effwidth = width;
+
+ if (flexFound) {
+ double remainingWidth = effwidth - width;
+ double widthPer = remainingWidth / sumFlex;
+
+ for (DomElement child : getDomElement().getChildren()) {
+ if (child.getWidget() instanceof Flexible) {
+ Size requiredSize = child.getLayouter().layout(child, new ConstraintBox(
+ 0, widthPer * ((Flexible) child.getWidget()).flex.getValue(),
+ crossAxisAlignment == CrossAxisAlignment.STRETCH ? constraintBox.getMaxHeight() : 0, constraintBox.getMaxHeight()
+ ));
+ saved.put(child, requiredSize);
+ height = Math.max(height, requiredSize.getHeight());
+ width += requiredSize.getWidth();
+ }
+ }
+ }
+
+
+ height = constraintBox.getMaxHeight() == Integer.MAX_VALUE ? height : constraintBox.getMaxHeight();
+
+
+
+ double startx = 0;
+ double widthDelta = 0;
+
+ if (mainAxisAlignment == MainAxisAlignment.CENTER)
+ startx = (effwidth - width) / 2;
+ else if (mainAxisAlignment == MainAxisAlignment.END)
+ startx = effwidth - width;
+ else if (mainAxisAlignment == MainAxisAlignment.SPACE_BETWEEN) {
+ double remaining = effwidth - width;
+ if (remaining > 0) {
+ startx = 0;
+ widthDelta = remaining / (getDomElement().getChildren().size()-1);
+ } else {
+ startx = (effwidth - width) / 2;
+ }
+ } else if (mainAxisAlignment == MainAxisAlignment.SPACE_EVENLY) {
+ double remaining = effwidth - width;
+ if (remaining > 0) {
+ widthDelta = remaining / (getDomElement().getChildren().size()+1);
+ startx = widthDelta;
+ } else {
+ startx = (effwidth - width) / 2;
+ }
+ } else if (mainAxisAlignment == MainAxisAlignment.SPACE_AROUND) {
+ double remaining = effwidth - width;
+ if (remaining > 0) {
+ widthDelta = 2 * remaining / getDomElement().getChildren().size();
+ startx = widthDelta / 2;
+ } else {
+ startx = (effwidth - width / 2);
+ }
+ }
+
+ for (DomElement child : getDomElement().getChildren()) {
+ Size size = saved.get(child);
+
+ child.setRelativeBound(new Rect(
+ startx,
+ crossAxisAlignment == CrossAxisAlignment.START ? 0 :
+ crossAxisAlignment == CrossAxisAlignment.CENTER ? (height-size.getHeight())/2 :
+ crossAxisAlignment == CrossAxisAlignment.STRETCH ? (height-size.getHeight())/2 :
+ height - size.getHeight(),size.getWidth(), size.getHeight()
+ ));
+ startx += size.getWidth();
+ startx += widthDelta;
+ }
+ return new Size(
+ effwidth,
+ height
+ );
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scaler.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scaler.java
new file mode 100644
index 00000000..b4682dc1
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scaler.java
@@ -0,0 +1,99 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Position;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Scaler extends AnnotatedExportOnlyWidget implements Layouter, Renderer {
+
+ @Export(attributeName = "scale")
+ public final BindableAttribute<Double> scale = new BindableAttribute<>(Double.class, 1.0);
+
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(child.getValue());
+ }
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ DomElement child = getDomElement().getChildren().get(0);
+ Size dims = child.getLayouter().layout(child, new ConstraintBox(
+ (constraintBox.getMinWidth() / scale.getValue()),
+ (constraintBox.getMaxWidth() / scale.getValue()),
+ (constraintBox.getMinHeight() / scale.getValue()),
+ (constraintBox.getMaxHeight() / scale.getValue())
+ ));
+ child.setRelativeBound(new Rect(0,0,
+ (dims.getWidth() * scale.getValue()),
+ (dims.getHeight() * scale.getValue())));
+ child.setSize(new Size(dims.getWidth(), dims.getHeight()));
+
+ return new Size(dims.getWidth() * scale.getValue(), dims.getHeight() * scale.getValue());
+ }
+
+ @Override
+ public Position transformPoint(DomElement element, Position pos) {
+ Rect elementRect = element.getRelativeBound();
+ double relX = pos.getX() - elementRect.getX();
+ double relY = pos.getY() - elementRect.getY();
+ return new Position(relX / scale.getValue(), relY / scale.getValue());
+ }
+
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ DomElement value = buildContext.getChildren().get(0);
+
+ Rect original = value.getRelativeBound();
+ GlStateManager.translate(original.getX(), original.getY(), 0);
+ GlStateManager.scale(scale.getValue(), scale.getValue(), 1);
+
+ double absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth();
+ double absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight();
+
+ Rect elementABSBound = new Rect(
+ (buildContext.getAbsBounds().getX() + original.getX() * absXScale),
+ (buildContext.getAbsBounds().getY() + original.getY() * absYScale),
+ (original.getWidth() * absXScale),
+ (original.getHeight() * absYScale)
+ );
+ value.setAbsBounds(elementABSBound);
+
+ value.getRenderer().doRender(absMouseX, absMouseY,
+ (relMouseX - original.getX()) / scale.getValue(),
+ (relMouseY - original.getY()) / scale.getValue(), partialTicks, context, value);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/ScrollablePanel.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/ScrollablePanel.java
new file mode 100644
index 00000000..97be0b0c
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/ScrollablePanel.java
@@ -0,0 +1,136 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Passthrough;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.minecraft.util.ResourceLocation;
+
+@Passthrough(exportName = "$", bindName = "$", type = Widget.class)
+public class ScrollablePanel extends AnnotatedWidget {
+ @Bind(variableName = "contentX")
+ public final BindableAttribute<Double> contentX = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "contentY")
+ public final BindableAttribute<Double> contentY = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "contentSize")
+ public final BindableAttribute<Size> contentSize = new BindableAttribute<>(Size.class);
+ @Bind(variableName = "contentWidth")
+ public final BindableAttribute<Double> contentWidth = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "contentHeight")
+ public final BindableAttribute<Double> contentHeight = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "viewportSize")
+ public final BindableAttribute<Size> viewportSize = new BindableAttribute<>(Size.class);
+ @Bind(variableName = "viewportWidth")
+ public final BindableAttribute<Double> viewportWidth = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "viewportHeight")
+ public final BindableAttribute<Double> viewportHeight = new BindableAttribute<>(Double.class);
+
+ @Bind(variableName = "x")
+ public final BindableAttribute<Double> x = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "y")
+ public final BindableAttribute<Double> y = new BindableAttribute<>(Double.class);
+
+ @Export(attributeName = "direction")
+ @Bind(variableName = "direction")
+ public final BindableAttribute<Direction> direction = new BindableAttribute<>(Direction.class);
+ @Bind(variableName = "verticalThickness")
+ @Export(attributeName = "verticalThickness")
+ public final BindableAttribute<Double> vertThickness = new BindableAttribute<>(Double.class);
+ @Bind(variableName = "horizontalThickness")
+ @Export(attributeName = "verticalThickness")
+ public final BindableAttribute<Double> horzThickness = new BindableAttribute<>(Double.class);
+
+ @Bind(variableName = "horzRef")
+ public final BindableAttribute<DomElement> horzRef = new BindableAttribute<>(DomElement.class);
+
+ @Bind(variableName = "vertRef")
+ public final BindableAttribute<DomElement> vertRef = new BindableAttribute<>(DomElement.class);
+ @AllArgsConstructor
+ @Getter
+ public enum Direction {
+ HORIZONTAL(true, false),
+ VERTICAL(false, true),
+ BOTH(true, true);
+
+ private boolean horizontal;
+ private boolean vertical;
+
+ }
+ public ScrollablePanel() {
+ super(new ResourceLocation("dungeonsguide:gui/elements/scrollablePanel.gui"));
+
+ contentSize.addOnUpdate((old, neu) -> {
+ contentWidth.setValue(neu.getWidth() - viewportWidth.getValue());
+ contentHeight.setValue(neu.getHeight() - viewportHeight.getValue());
+ });
+
+ viewportSize.addOnUpdate((old, neu) -> {
+ viewportWidth.setValue(neu.getWidth());
+ viewportHeight.setValue(neu.getHeight());
+ contentWidth.setValue(contentSize.getValue().getWidth() - viewportWidth.getValue());
+ contentHeight.setValue(contentSize.getValue().getHeight() - viewportHeight.getValue());
+ });
+
+ x.addOnUpdate((old, neu) -> {
+ if (direction.getValue().isHorizontal())
+ contentX.setValue(-neu);
+ });
+ y.addOnUpdate((old, neu) ->{
+ if (direction.getValue().isVertical())
+ contentY.setValue(-neu);
+ });
+ direction.addOnUpdate((old, neu) -> {
+ vertThickness.setValue(neu.isVertical() ? 7 : 0.0);
+ horzThickness.setValue(neu.isHorizontal() ? 7 :0.0);
+ });
+
+ }
+
+ @Override
+ public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) {
+ return false;
+ }
+
+ @Override
+ public boolean mouseScrolled(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) {
+ if (direction.getValue().vertical) {
+ double old = this.y.getValue(), neu;
+ this.y.setValue(
+ neu = Layouter.clamp(this.y.getValue() + scrollAmount,0, contentHeight.getValue())
+ );
+ return old != neu;
+ } else if (direction.getValue().horizontal) {
+ double old = this.x.getValue(), neu;
+ this.x.setValue(
+ neu = Layouter.clamp(this.x.getValue() + scrollAmount,0, contentWidth.getValue())
+ );
+ return old != neu;
+ }
+ return false;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scrollbar.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scrollbar.java
new file mode 100644
index 00000000..42bc1824
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Scrollbar.java
@@ -0,0 +1,166 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.OnlyChildrenRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Passthrough;
+import net.minecraft.util.ResourceLocation;
+
+@Passthrough(exportName = "$track", bindName = "track", type = Widget.class)
+@Passthrough(exportName = "$thumb", bindName = "thumb", type = Widget.class)
+public class Scrollbar extends AnnotatedWidget {
+ // to set location and stuff
+ @Bind(variableName = "x")
+ public final BindableAttribute<Double> thumbX = new BindableAttribute<>(Double.class, 0.0);
+ @Bind(variableName = "y")
+ public final BindableAttribute<Double> thumbY = new BindableAttribute<>(Double.class, 0.0);
+ @Bind(variableName = "width")
+ public final BindableAttribute<Double> width = new BindableAttribute<>(Double.class, Double.POSITIVE_INFINITY);
+ @Bind(variableName = "height")
+ public final BindableAttribute<Double> height = new BindableAttribute<>(Double.class, Double.POSITIVE_INFINITY);
+ @Bind(variableName = "size")
+ public final BindableAttribute<Size> size = new BindableAttribute<>(Size.class);
+
+ @Export(attributeName = "thumbValue")
+ public final BindableAttribute<Double> thumbValue = new BindableAttribute<>(Double.class, 10.0);
+
+
+ @Export(attributeName = "minThumbSize")
+ public final BindableAttribute<Double> minimumThumbSize = new BindableAttribute<>(Double.class, 10.0);
+
+ @Export(attributeName = "min")
+ public final BindableAttribute<Double> min = new BindableAttribute<>(Double.class, 0.0);
+ @Export(attributeName = "max")
+ public final BindableAttribute<Double> max = new BindableAttribute<>(Double.class, 100.0);
+ @Export(attributeName = "current")
+ public final BindableAttribute<Double> current = new BindableAttribute<>(Double.class, 0.0);
+
+ @Export(attributeName = "orientation")
+ public final BindableAttribute<Line.Orientation> orientation = new BindableAttribute<>(Line.Orientation.class);
+
+ @Override
+ protected Renderer createRenderer() {
+ return OnlyChildrenRenderer.INSTANCE;
+ }
+
+ public Scrollbar() {
+ super(new ResourceLocation("dungeonsguide:gui/elements/scrollbar.gui"));
+
+ thumbValue.addOnUpdate(this::updateStuff);
+ min.addOnUpdate(this::updateStuff);
+ max.addOnUpdate(this::updateStuff);
+ current.addOnUpdate(this::updateThumbLocation);
+ size.addOnUpdate((a,b) -> this.updateStuff(0,0));
+ updateStuff(0, 0);
+ }
+
+ private double per1, per2;
+
+ private void updateStuff(double _, double __) {
+ if (getDomElement().getSize() != null)
+ updatePers(getDomElement().getSize());
+ updateThumbLocation(0, 0);
+ }
+ private void updatePers(Size size) {
+
+ double min = this.min.getValue();
+ double max = this.max.getValue();
+ double thumbSize = this.thumbValue.getValue();
+
+ double movingLength = orientation.getValue() == Line.Orientation.VERTICAL ? size.getHeight() : size.getWidth();
+
+
+ per1 = movingLength / (max - min + thumbSize);
+ per2 = (max - min + thumbSize) / movingLength;
+
+ if (per1 * thumbSize > minimumThumbSize.getValue()) {
+ if (orientation.getValue() == Line.Orientation.VERTICAL) height.setValue(per1 * thumbSize);
+ else width.setValue(per1 * thumbSize);
+ } else {
+ movingLength -= minimumThumbSize.getValue();
+ per1 = movingLength / (max - min);
+ per2 = (max - min) / movingLength;
+
+ if (orientation.getValue() == Line.Orientation.VERTICAL) height.setValue(minimumThumbSize.getValue());
+ else width.setValue(minimumThumbSize.getValue());
+ }
+ }
+
+ private void updateThumbLocation(double newCurrent, double newMin) {
+ double location = per1 * (current.getValue() - min.getValue());
+
+ if (orientation.getValue() == Line.Orientation.VERTICAL) thumbY.setValue(location);
+ else thumbX.setValue(location);
+ }
+
+
+ private double movingDir;
+ private double startCurr;
+ private boolean moving = false;
+ @Override
+ public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) {
+ getDomElement().obtainFocus();
+ if (orientation.getValue() == Line.Orientation.VERTICAL) movingDir = relMouseY;
+ else movingDir = relMouseX;
+
+ startCurr = current.getValue();
+ moving = true;
+ return true;
+ }
+
+ @Override
+ public void mouseClickMove(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int clickedMouseButton, long timeSinceLastClick) {
+ double old = movingDir;
+ double movingDir = 0;
+ if (orientation.getValue() == Line.Orientation.VERTICAL) movingDir = relMouseY;
+ else movingDir = relMouseX;
+
+ double dThing = movingDir - old;
+ double newVal = dThing * per2 + startCurr;
+ newVal = Layouter.clamp(newVal, min.getValue(), max.getValue());
+ this.current.setValue(newVal);
+ }
+
+ @Override
+ public void mouseReleased(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int state) {
+ moving = false;
+ System.out.println(getDomElement().isFocused());
+ }
+
+ @Override
+ public boolean mouseScrolled(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) {
+ if (moving) return false;
+
+// double old = this.current.getValue();
+// double neu;
+// this.current.setValue(
+// neu = Layouter.clamp(this.current.getValue() + scrollAmount, min.getValue(), max.getValue())
+// );
+// return old != neu;
+ return false;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/SizedBox.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/SizedBox.java
new file mode 100644
index 00000000..37a277a5
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/SizedBox.java
@@ -0,0 +1,72 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.SingleChildRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.List;
+
+public class SizedBox extends AnnotatedExportOnlyWidget implements Layouter {
+
+ @Export(attributeName = "width")
+ public final BindableAttribute<Double> width = new BindableAttribute<>(Double.class, Double.POSITIVE_INFINITY);
+ @Export(attributeName = "height")
+ public final BindableAttribute<Double> height = new BindableAttribute<>(Double.class, Double.POSITIVE_INFINITY);
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class);
+
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return child.getValue() == null ? Collections.EMPTY_LIST : Collections.singletonList(child.getValue());
+ }
+
+ public SizedBox() {
+ width.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ height.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+
+ double width = Layouter.clamp(this.width.getValue(), constraintBox.getMinWidth(), constraintBox.getMaxWidth());
+ double height = Layouter.clamp(this.height.getValue(), constraintBox.getMinHeight(), constraintBox.getMaxHeight());
+
+ if (getDomElement().getChildren().isEmpty()) {
+ return new Size(width, height);
+ }
+
+ DomElement child = getDomElement().getChildren().get(0);
+ Size dim = child.getLayouter().layout(child, new ConstraintBox(
+ width, width, height, height
+ )); // force size heh.
+ child.setRelativeBound(new Rect(0,0,dim.getWidth(),dim.getHeight()));
+ return dim;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Slot.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Slot.java
new file mode 100644
index 00000000..0b81262c
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Slot.java
@@ -0,0 +1,43 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Slot extends AnnotatedExportOnlyWidget {
+ @Export(attributeName = "child")
+ public final BindableAttribute<Widget> replacement = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> original = new BindableAttribute<>(Widget.class);
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ if (replacement.getValue() != null) return Collections.singletonList(replacement.getValue());
+ if (original.getValue() != null) return Collections.singletonList(original.getValue());
+ return Collections.EMPTY_LIST;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Stack.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Stack.java
new file mode 100644
index 00000000..cb5e492f
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Stack.java
@@ -0,0 +1,98 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.OnlyChildrenRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.WidgetList;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.List;
+
+public class Stack extends AnnotatedExportOnlyWidget implements Renderer {
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ for (int i = buildContext.getChildren().size() - 1; i >= 0; i --) {
+ DomElement value = buildContext.getChildren().get(i);
+ Rect original = value.getRelativeBound();
+ GlStateManager.pushMatrix();
+ GlStateManager.translate(original.getX(), original.getY(), 0);
+
+ double absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth();
+ double absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight();
+
+ Rect elementABSBound = new Rect(
+ (buildContext.getAbsBounds().getX() + original.getX() * absXScale),
+ (buildContext.getAbsBounds().getY() + original.getY() * absYScale),
+ (original.getWidth() * absXScale),
+ (original.getHeight() * absYScale)
+ );
+ value.setAbsBounds(elementABSBound);
+
+ if (i > 0)
+ value.getRenderer().doRender(-1, -1, -1, -1, partialTicks, context, value);
+ if (i == 0)
+ value.getRenderer().doRender(absMouseX, absMouseY,
+ relMouseX - original.getX(),
+ relMouseY - original.getY(), partialTicks, context, value);
+ GlStateManager.popMatrix();
+ }
+ }
+
+ public static class StackingLayouter implements Layouter {
+ public static final StackingLayouter INSTANCE = new StackingLayouter();
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ double maxW = 0, maxH = 0;
+ for (DomElement child : buildContext.getChildren()) {
+ Size dim = child.getLayouter().layout(child, constraintBox);
+ if (maxW< dim.getWidth()) maxW = dim.getWidth();
+ if (maxH< dim.getHeight()) maxH = dim.getHeight();
+ child.setRelativeBound(new Rect(0,0,dim.getWidth(), dim.getHeight()));
+ }
+ return new Size(maxW, maxH);
+ }
+ }
+
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<WidgetList> widgets = new BindableAttribute<>(WidgetList.class);
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return widgets.getValue();
+ }
+
+ @Override
+ protected Layouter createLayouter() {
+ return StackingLayouter.INSTANCE;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Text.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Text.java
new file mode 100644
index 00000000..c03f06ea
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/Text.java
@@ -0,0 +1,214 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Text extends AnnotatedExportOnlyWidget implements Layouter, Renderer {
+ @Export(attributeName = "text")
+ public final BindableAttribute<String> text = new BindableAttribute<>(String.class, "");
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Data @AllArgsConstructor
+ public static class WrappedTextData {
+ final int width;
+ final String text;
+ }
+ public List<WrappedTextData> wrappedTexts = new ArrayList();
+ @Export(attributeName = "font")
+ public final BindableAttribute<FontRenderer> fontRenderer =new BindableAttribute<>(FontRenderer.class, Minecraft.getMinecraft().fontRendererObj);
+
+ public static enum WordBreak {
+ NEVER, WORD, LETTER
+ }
+ @Export(attributeName = "break")
+ public final BindableAttribute<WordBreak> wordBreak = new BindableAttribute<>(WordBreak.class, WordBreak.WORD);
+
+ @Export(attributeName = "lineSpacing")
+ public final BindableAttribute<Double> lineSpacing = new BindableAttribute<>(Double.class, 1.0);
+
+
+ public static enum TextAlign {
+ LEFT, CENTER, RIGHT
+ }
+ @Export(attributeName = "align")
+ public final BindableAttribute<TextAlign> textAlign = new BindableAttribute<>(TextAlign.class, TextAlign.LEFT);
+
+ @Export(attributeName = "color")
+ public final BindableAttribute<Integer> color = new BindableAttribute<>(Integer.class, 0xFF000000);
+
+ public Text() {
+ text.addOnUpdate((a,b) -> getDomElement().requestRelayout());
+ fr = Minecraft.getMinecraft().fontRendererObj;
+ }
+ private FontRenderer fr;
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ int y = 0;
+ int color = this.color.getValue();
+ int yInc = (int) (fr.FONT_HEIGHT * lineSpacing.getValue());
+ double width =buildContext.getSize().getWidth();
+
+ GlStateManager.enableTexture2D();
+ if (textAlign.getValue() == TextAlign.LEFT) {
+ for (WrappedTextData wrappedText : wrappedTexts) {
+ fr.drawString(wrappedText.text, 0, y, color);
+ y += yInc;
+ }
+ } else if (textAlign.getValue() == TextAlign.CENTER) {
+ for (WrappedTextData wrappedText : wrappedTexts) {
+ fr.drawString(wrappedText.text, (int) ((width-wrappedText.getWidth())/2), y, color);
+ y += yInc;
+ }
+ } else {
+ for (WrappedTextData wrappedText : wrappedTexts) {
+ fr.drawString(wrappedText.text, (int) (width - wrappedText.getWidth()), y, color);
+ y += yInc;
+ }
+ }
+ }
+
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ wrappedTexts.clear();
+
+ FontRenderer fr = fontRenderer.getValue();
+ String text = this.text.getValue();
+
+ boolean hadToWrap = false;
+ int maxWidth2 = 0;
+
+
+ WordBreak wordBreak = this.wordBreak.getValue();
+
+ String[] splitByLine = text.split("\n");
+ for (String line : splitByLine) {
+ String[] splitByWord = line.split(" ");
+ double maxWidth = constraintBox.getMaxWidth();
+ int currentWidth = 0;
+ boolean added = false;
+ StringBuilder currentLine = new StringBuilder();
+ for (String s : splitByWord) {
+ int strWidth = fr.getStringWidth((added ? " " : "") +s);
+ if (strWidth + currentWidth <= maxWidth) {
+ if (added) currentLine.append(" ");
+ currentLine.append(s);
+ added = true;
+ currentWidth += strWidth;
+ } else {
+ hadToWrap = true;
+ // need to break word.
+ if (wordBreak == WordBreak.WORD) {
+ String current = s;
+ if (fr.getStringWidth(s) > maxWidth) {
+ // there is no hope. just continue.
+ current = currentLine.toString()+ " "+s;
+ } else {
+ wrappedTexts.add(new WrappedTextData(currentWidth, currentLine.toString()));
+ current = s;
+ }
+
+ // binary search unsplittable.
+ while (fr.getStringWidth(current) > maxWidth) {
+ currentLine = new StringBuilder("");
+ String remaining = "";
+ currentWidth = 0;
+ double remainingWidth = maxWidth - currentWidth;
+ while(current.length() > 1 && remainingWidth > 4) {
+ String query = current.substring(0, current.length()/2);
+ int len = fr.getStringWidth(query);
+ if (len <= remainingWidth) {
+ currentLine.append(query);
+ remainingWidth -= len;
+ current = current.substring(current.length() / 2);
+ } else {
+ remaining = current.substring(current.length() / 2) + remaining;
+ current = query;
+ }
+ }
+ remaining = current + remaining;
+
+ wrappedTexts.add(new WrappedTextData((int) (maxWidth - remainingWidth), currentLine.toString()));
+
+ current = remaining;
+ }
+ currentLine = new StringBuilder(current);
+ currentWidth = fr.getStringWidth(current);
+ } else if (wordBreak == WordBreak.NEVER) {
+ currentLine.append(" ").append(s);
+ currentWidth += strWidth;
+ break;
+ } else {
+ // binary search correct length-
+ String current = " "+s;
+ double remainingWidth = maxWidth - currentWidth;
+ String remaining = "";
+ while(current.length() > 1 && remainingWidth > 4) {
+ String query = current.substring(0, current.length()/2);
+ int len = fr.getStringWidth(query);
+ if (len <= remainingWidth) {
+ currentLine.append(query);
+ remainingWidth -= len;
+ current = current.substring(current.length() / 2);
+ } else {
+ remaining = current.substring(current.length() / 2) + remaining;
+ current = query;
+ }
+ }
+ remaining = current + remaining;
+
+ wrappedTexts.add(new WrappedTextData((int) (maxWidth - remainingWidth), currentLine.toString()));
+ currentLine = new StringBuilder(remaining);
+ currentWidth = fr.getStringWidth(remaining);
+ }
+ }
+ }
+ if (currentWidth > maxWidth2) maxWidth2 = currentWidth;
+ wrappedTexts.add(new WrappedTextData(currentWidth, currentLine.toString()));
+ }
+
+
+
+ return new Size(hadToWrap ? constraintBox.getMaxWidth() :
+ Layouter.clamp(maxWidth2, constraintBox.getMinWidth(), constraintBox.getMaxWidth()),
+ Layouter.clamp( (fr.FONT_HEIGHT * lineSpacing.getValue()) * wrappedTexts.size(), constraintBox.getMinHeight(), constraintBox.getMaxHeight()));
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/TextField.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/TextField.java
new file mode 100644
index 00000000..fedc9fc2
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/TextField.java
@@ -0,0 +1,433 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.MathHelper;
+import org.lwjgl.input.Keyboard;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+public class TextField extends AnnotatedExportOnlyWidget implements Renderer, Layouter {
+
+ @Export(
+ attributeName = "value"
+ )
+ public final BindableAttribute<String> value = new BindableAttribute<>(String.class, "");
+
+ @Export(
+ attributeName = "placeholder"
+ )
+ public final BindableAttribute<String> placeholder = new BindableAttribute<>(String.class, "");
+
+ @Export(
+ attributeName = "color"
+ )
+ public final BindableAttribute<Integer> color = new BindableAttribute<>(Integer.class, 0xFFFFFFFF);
+
+
+ @Export(
+ attributeName = "placeholderColor"
+ )
+ public final BindableAttribute<Integer> placeholderColor = new BindableAttribute<>(Integer.class, 0xFFAAAAAA);
+
+
+ private int selectionStart = 0;
+ private int selectionEnd = 0;
+
+ private int cursor = 0;
+
+ private double xOffset = 0;
+
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.EMPTY_LIST;
+ }
+
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ return new Size(constraintBox.getMaxWidth(), Layouter.clamp(15, constraintBox.getMinHeight(), constraintBox.getMaxHeight()));
+ }
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ Size bounds = getDomElement().getSize();
+
+ context.drawRect(0,0,bounds.getWidth(), bounds.getHeight(), getDomElement().isFocused() ? Color.white.getRGB() : Color.gray.getRGB());
+ context.drawRect(1,1,bounds.getWidth() - 1, bounds.getHeight() - 1, Color.black.getRGB());
+
+ Minecraft mc = Minecraft.getMinecraft();
+ context.pushClip(buildContext.getAbsBounds(), bounds, 1, 1, bounds.getWidth() -2, bounds.getHeight()-2);
+
+ String text = value.getValue();
+ FontRenderer fr = mc.fontRendererObj;
+ int y = (int) ((bounds.getHeight() - fr.FONT_HEIGHT) / 2);
+ GlStateManager.enableTexture2D();
+ fr.drawString(value.getValue(), (int) (3 - xOffset), y, color.getValue());
+ if (text.isEmpty())
+ fr.drawString(placeholder.getValue(), 3, y, placeholderColor.getValue());
+ // draw selection
+ if (getDomElement().isFocused()) {
+ if (selectionStart != -1) {
+ int startX = (int) (fr.getStringWidth(text.substring(0, selectionStart)) - xOffset);
+ int endX = (int) (fr.getStringWidth(text.substring(0, selectionEnd)) - xOffset);
+ Gui.drawRect( (3 + startX), y, (3 + endX), y + fr.FONT_HEIGHT, 0xFF00FF00);
+ GlStateManager.enableTexture2D();
+ fr.drawString(text.substring(selectionStart, selectionEnd), (int) (3 + startX), y, color.getValue());
+ }
+
+ // draw cursor
+ if (cursor != -1) {
+ if (cursor > text.length()) setCursor0(text.length());
+ int x = (int) (fr.getStringWidth(text.substring(0, cursor)) - xOffset);
+
+ if (System.currentTimeMillis() % 1500 < 750)
+ Gui.drawRect(3 + x, y, 4 + x, y + fr.FONT_HEIGHT, 0xFFFFFFFF);
+ }
+ }
+ context.popClip();
+ }
+
+ private void setCursor0(int cursor) {
+ if (cursor > value.getValue().length()) cursor = value.getValue().length();
+ if (cursor < 0) cursor = 0;
+ this.cursor = cursor;
+
+
+ int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(value.getValue().substring(0, cursor));
+ double cursorX = width + 3- xOffset;
+ cursorX = MathHelper.clamp_double(cursorX,10, getDomElement().getSize().getWidth() - 10);
+ xOffset = width+ 3 - cursorX;
+ xOffset = MathHelper.clamp_double(xOffset, 0,Math.max(0, Minecraft.getMinecraft().fontRendererObj.getStringWidth(value.getValue()) - getDomElement().getSize().getWidth()+10));
+ }
+
+ @Override
+ public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) {
+ getDomElement().obtainFocus();
+ Rect actualField = new Rect(1, 3,
+ getDomElement().getSize().getWidth() - 2,
+ getDomElement().getSize().getHeight() - 6);
+ if (!actualField.contains(relMouseX, relMouseY)) return false;
+
+
+
+ double relStartT = relMouseX-3;
+ double offseted = relStartT + xOffset;
+
+ selectionStart = -1;
+
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+
+ for (int i = 0; i < value.getValue().length(); i++) {
+ int totalWidth = fr.getStringWidth(value.getValue().substring(0, i));
+ if (offseted < totalWidth) {
+ setCursor0(i);
+ return true;
+ }
+ }
+ setCursor0(value.getValue().length());
+ return true;
+ }
+
+ @Override
+ public void mouseClickMove(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int clickedMouseButton, long timeSinceLastClick) {
+ if (!getDomElement().isFocused()) return;
+ selectionStart = cursor;
+ selectionEnd = cursor;
+
+ double relStartT = relMouseX-3;
+ double offseted = relStartT + xOffset;
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+
+ for (int i = 0; i < value.getValue().length(); i++) {
+ int totalWidth = fr.getStringWidth(value.getValue().substring(0, i));
+ if (offseted < totalWidth) {
+ if (i < cursor) {
+ selectionStart = i;
+ selectionEnd = cursor;
+ } else {
+ selectionStart = cursor;
+ selectionEnd = i;
+ }
+ return;
+ }
+ }
+ selectionEnd = value.getValue().length();
+ if (selectionStart == selectionEnd) {
+ selectionStart = -1;
+ }
+ }
+
+ @Override
+ public boolean mouseScrolled(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) {
+ if (!getDomElement().isFocused()) return false;
+ double lastOffset = xOffset;
+ if (scrollAmount > 0) {
+ xOffset += 5;
+ } else if (scrollAmount < 0){
+ xOffset -= 5;
+ }
+ if (xOffset < 0) {
+ xOffset = 0;
+ }
+ int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(value.getValue());
+ double overflow = getDomElement().getSize().getWidth() - 3 - width;
+
+
+ if (overflow >= 0) {
+ xOffset = 0;
+ } else if (width - xOffset + 10 < getDomElement().getSize().getWidth()) {
+ xOffset = width - getDomElement().getSize().getWidth()+10;
+ }
+ return lastOffset != xOffset;
+ }
+
+ @Override
+ public void keyHeld(char typedChar, int keyCode) {
+ this.keyPressed(typedChar, keyCode);
+ }
+
+ @Override
+ public void keyPressed(char typedChar, int keycode) {
+ if (selectionStart == -1) {
+ if (keycode == 199) { // home
+ setCursor0(0);
+ xOffset = 0;
+ return;
+ }
+
+ if (keycode == 207) { // end
+ setCursor0(value.getValue().length());
+
+ int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(value.getValue());
+ xOffset = Math.max(0, width - getDomElement().getSize().getWidth()+10);
+ return;
+ }
+
+ if (keycode == 203) { // left
+ setCursor0(this.cursor-1);;
+ if (cursor < 0) setCursor0(0);
+ return;
+ }
+
+ if (keycode == 205) { // right
+ setCursor0(this.cursor+1);
+ if (cursor > value.getValue().length()) setCursor0(value.getValue().length());
+ return;
+ }
+
+ // backspace
+ if (keycode == 14 && cursor > 0) {
+ value.setValue(this.value.getValue().substring(0, cursor-1) + this.value.getValue().substring(cursor));
+ setCursor0(this.cursor-1);
+ return;
+ }
+
+ //del
+ if (keycode == 211 && cursor < value.getValue().length()) {
+ value.setValue(this.value.getValue().substring(0, cursor) + this.value.getValue().substring(cursor+1));
+ return;
+ }
+
+ // paste
+ boolean shouldPaste = false;
+ if (keycode == 47) {
+ if (Minecraft.isRunningOnMac) { // mac
+ if (Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)) {
+ shouldPaste = true;
+ }
+ } else { // literally everything else
+ if (Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157)) {
+ shouldPaste = true;
+ }
+ }
+ }
+ if (shouldPaste) {
+ Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
+ if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ try {
+ Object theText = transferable.getTransferData(DataFlavor.stringFlavor);
+ value.setValue(
+ this.value.getValue().substring(0, this.cursor)
+ + theText
+ + this.value.getValue().substring(this.cursor));
+
+ cursor += theText.toString().length();
+ } catch (UnsupportedFlavorException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return;
+ }
+
+ // text
+ if (isPrintableChar(typedChar)) {
+ value.setValue(
+ this.value.getValue().substring(0, this.cursor)
+ + typedChar
+ + this.value.getValue().substring(this.cursor));
+ this.setCursor0(this.cursor+1);;
+ return;
+ }
+ } else {
+ if (keycode == 199) { // home
+ setCursor0(0);
+ selectionStart = -1;
+ xOffset =0;
+ return;
+ }
+
+ if (keycode == 207) { // end
+ selectionStart = -1;
+ setCursor0(value.getValue().length());
+ int width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(value.getValue());
+ xOffset = Math.max(0, width - getDomElement().getSize().getWidth()+10);
+ return;
+ }
+
+ if (keycode == 203) { // left
+ setCursor0(selectionStart);
+ selectionStart = -1;
+ return;
+ }
+
+ if (keycode == 205) { // right
+ setCursor0(selectionEnd);
+ selectionStart = -1;
+ return;
+ }
+
+ // backspace
+ if (keycode == 14 && cursor > 0) {
+ value.setValue(this.value.getValue().substring(0, selectionStart) + this.value.getValue().substring(selectionEnd));
+ setCursor0(selectionStart);
+ selectionStart = -1;
+ return;
+ }
+
+ //del
+ if (keycode == 211 && cursor < value.getValue().length()) {
+ value.setValue(this.value.getValue().substring(0, selectionStart) + this.value.getValue().substring(selectionEnd));
+ setCursor0(selectionStart);
+ selectionStart = -1;
+ return;
+ }
+
+ // paste
+ boolean shouldPaste = false;
+ if (keycode == 47) {
+ if (Minecraft.isRunningOnMac) { // mac
+ if (Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)) {
+ shouldPaste = true;
+ }
+ } else { // literally everything else
+ if (Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157)) {
+ shouldPaste = true;
+ }
+ }
+ }
+ if (shouldPaste) {
+ Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
+ if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ try {
+ Object theText = transferable.getTransferData(DataFlavor.stringFlavor);
+ value.setValue(
+ this.value.getValue().substring(0, this.selectionStart)
+ + theText
+ + this.value.getValue().substring(this.selectionEnd));
+ setCursor0(this.selectionStart + theText.toString().length());
+ } catch (UnsupportedFlavorException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ selectionStart = -1;
+ }
+ return;
+ }
+ boolean shouldCopy = false;
+ if (keycode == 46) {
+ if (Minecraft.isRunningOnMac) { // mac
+ if (Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)) {
+ shouldCopy = true;
+ }
+ } else { // literally everything else
+ if (Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157)) {
+ shouldCopy = true;
+ }
+ }
+ }
+ if (shouldCopy) {
+ StringSelection selection = new StringSelection(value.getValue().substring(selectionStart, selectionEnd));
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(selection, selection);
+ return;
+ }
+
+ // text
+ if (isPrintableChar(typedChar)) {
+ value.setValue(
+ this.value.getValue().substring(0, this.selectionStart)
+ + typedChar
+ + this.value.getValue().substring(this.selectionEnd));
+ setCursor0(this.selectionStart + 1);
+ selectionStart = -1;
+ return;
+ }
+ }
+ }
+ public boolean isPrintableChar( char c ) {
+ Character.UnicodeBlock block = Character.UnicodeBlock.of( c );
+ return (!Character.isISOControl(c)) &&
+ c != KeyEvent.CHAR_UNDEFINED &&
+ block != null &&
+ block != Character.UnicodeBlock.SPECIALS;
+ }
+
+ @Override
+ public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) {
+ if (getDomElement().getAbsBounds().contains(absMouseX, absMouseY))
+ getDomElement().setCursor(EnumCursor.BEAM_CURSOR);
+ return true;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/UnconstrainedBox.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/UnconstrainedBox.java
new file mode 100644
index 00000000..ed1ff211
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/UnconstrainedBox.java
@@ -0,0 +1,60 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+
+import java.util.Collections;
+import java.util.List;
+
+public class UnconstrainedBox extends AnnotatedExportOnlyWidget implements Layouter {
+
+
+ @Export(attributeName = "$")
+ public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class);
+ @Export(attributeName = "direction")
+ public final BindableAttribute<ScrollablePanel.Direction> direction = new BindableAttribute<>(ScrollablePanel.Direction.class, ScrollablePanel.Direction.BOTH);
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(widget.getValue());
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ if (buildContext.getChildren().isEmpty()) {
+ return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ }
+ DomElement childCtx = buildContext.getChildren().get(0);
+
+ Size dim = childCtx.getLayouter().layout(childCtx, new ConstraintBox(0,
+ direction.getValue().isHorizontal() ? Double.POSITIVE_INFINITY : constraintBox.getMaxWidth(),
+ 0, direction.getValue().isVertical() ? Double.POSITIVE_INFINITY : constraintBox.getMaxHeight()));
+ childCtx.setRelativeBound(new Rect(0,0, dim.getWidth(), dim.getHeight()));
+ return new Size(dim.getWidth(), dim.getHeight());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/Image.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/Image.java
new file mode 100644
index 00000000..5bdcfa0c
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/Image.java
@@ -0,0 +1,63 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.image;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import lombok.AllArgsConstructor;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+
+import java.util.Collections;
+import java.util.List;
+
+@AllArgsConstructor
+public class Image extends Widget implements Renderer {
+ public final ResourceLocation location;
+ public final int uvX;
+ public final int uvY ;
+ public final int textureWidth;
+ public final int textureHeight;
+ public final int uvWidth;
+ public final int uvHeight;
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(location);
+ context.drawScaledCustomSizeModalRect(0, 0,
+ uvX,
+ uvY,
+ uvWidth,
+ uvHeight,
+ buildContext.getSize().getWidth(),
+ buildContext.getSize().getHeight(),
+ textureWidth,
+ textureHeight);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/ResourceImage.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/ResourceImage.java
new file mode 100644
index 00000000..d42c2f18
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/image/ResourceImage.java
@@ -0,0 +1,54 @@
+/*
+ * Dungeons Guide - The most Integerelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.image;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedExportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import net.minecraft.util.ResourceLocation;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ResourceImage extends AnnotatedExportOnlyWidget {
+
+ @Export(attributeName="location")
+ public final BindableAttribute<String > location = new BindableAttribute<String >(String.class);
+ @Export(attributeName="uvX")
+ public final BindableAttribute<Integer> uvX = new BindableAttribute<Integer>(Integer.class);
+ @Export(attributeName="uvY")
+ public final BindableAttribute<Integer> uvY = new BindableAttribute<Integer>(Integer.class);
+ @Export(attributeName="textureWidth")
+ public final BindableAttribute<Integer> textureWidth = new BindableAttribute<Integer>(Integer.class, 256);
+ @Export(attributeName="textureHeight")
+ public final BindableAttribute<Integer> textureHeight = new BindableAttribute<Integer>(Integer.class, 256);
+ @Export(attributeName="uvWidth")
+ public final BindableAttribute<Integer> uvWidth = new BindableAttribute<Integer>(Integer.class);
+ @Export(attributeName="uvHeight")
+ public final BindableAttribute<Integer> uvHeight = new BindableAttribute<Integer>(Integer.class);
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.singletonList(new Image(
+ new ResourceLocation(location.getValue()), uvX.getValue(), uvY.getValue(), textureWidth.getValue(), textureHeight.getValue(), uvWidth.getValue(), uvHeight.getValue()
+ ));
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/Layouter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/Layouter.java
new file mode 100644
index 00000000..5330e222
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/Layouter.java
@@ -0,0 +1,32 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.layouter;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+
+public interface Layouter {
+ public abstract Size layout(DomElement buildContext, ConstraintBox constraintBox);
+ public static double clamp(double val, double min, double max) {
+ if (val < min) return min;
+ if (val > max) return max;
+ return val;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/NullLayouter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/NullLayouter.java
new file mode 100644
index 00000000..23ce78de
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/NullLayouter.java
@@ -0,0 +1,33 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.layouter;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+
+public class NullLayouter implements Layouter {
+ public static final NullLayouter INSTANCE = new NullLayouter();
+ private NullLayouter() {}
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/SingleChildPassingLayouter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/SingleChildPassingLayouter.java
new file mode 100644
index 00000000..571e473d
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/layouter/SingleChildPassingLayouter.java
@@ -0,0 +1,42 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.layouter;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+
+public class SingleChildPassingLayouter implements Layouter {
+ public static final SingleChildPassingLayouter INSTANCE = new SingleChildPassingLayouter();
+ private SingleChildPassingLayouter() {}
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ if (buildContext.getChildren().isEmpty()) {
+ return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight());
+ }
+
+ DomElement childCtx = buildContext.getChildren().get(0);
+
+ Size dim = childCtx.getLayouter().layout(childCtx, constraintBox);
+ childCtx.setRelativeBound(new Rect(0,0, dim.getWidth(), dim.getHeight()));
+ return new Size(dim.getWidth(), dim.getHeight());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Animation.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Animation.java
new file mode 100644
index 00000000..60314d47
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Animation.java
@@ -0,0 +1,38 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+public class Animation<T> {
+ private long startMillies;
+ private double lengthMillies;
+
+ private T start;
+ private T end;
+
+ public Animation(long startMillies, double lengthMillies, T start, T end) {
+ this.startMillies = startMillies;
+ this.lengthMillies = lengthMillies;
+ this.start = start;
+ this.end = end;
+ }
+
+ public double getPhase() {
+ return (System.currentTimeMillis() - startMillies) / lengthMillies;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ConstraintBox.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ConstraintBox.java
new file mode 100644
index 00000000..af3465d6
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ConstraintBox.java
@@ -0,0 +1,40 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+// Idea heavily taken from flutter.
+@AllArgsConstructor @Getter
+public class ConstraintBox {
+ private double minWidth;
+ private double maxWidth;
+ private double minHeight;
+ private double maxHeight;
+
+ public static ConstraintBox loose(double width, double height) {
+ return new ConstraintBox(0,width,0,height);
+ }
+
+ public static ConstraintBox tight(double width, double height) {
+ return new ConstraintBox(width, width, height, height);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IPosition.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IPosition.java
new file mode 100644
index 00000000..e5f36ace
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IPosition.java
@@ -0,0 +1,25 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+public interface IPosition {
+ double getX();
+
+ double getY();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IRect.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IRect.java
new file mode 100644
index 00000000..3581af33
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/IRect.java
@@ -0,0 +1,34 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+public interface IRect {
+ default boolean contains(double x, double y) {
+ return getX() <= x && x < getX() + getWidth() &&
+ getY() <= y && y < getY()+ getHeight();
+ }
+
+ double getX();
+
+ double getY();
+
+ double getWidth();
+
+ double getHeight();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ISize.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ISize.java
new file mode 100644
index 00000000..2e4e3209
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/ISize.java
@@ -0,0 +1,29 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+public interface ISize {
+ double getWidth();
+
+ double getHeight();
+
+ default boolean contains(double x, double y) {
+ return 0 <= x && 0 <= y && x < getWidth() && y < getHeight();
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Position.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Position.java
new file mode 100644
index 00000000..01c884df
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Position.java
@@ -0,0 +1,28 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data @AllArgsConstructor
+public class Position implements IPosition {
+ public final double x;
+ public final double y;
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Rect.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Rect.java
new file mode 100644
index 00000000..37b31b75
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Rect.java
@@ -0,0 +1,32 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data @AllArgsConstructor
+public class Rect implements IRect {
+ private final double x;
+ private final double y;
+ private final double width;
+ private final double height;
+
+ public static Rect fromPositionSize(Position pos, Size size) { return new Rect(pos.getX(), pos.getY(), size.getWidth(), size.getHeight()); }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Size.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Size.java
new file mode 100644
index 00000000..d139870f
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/primitive/Size.java
@@ -0,0 +1,28 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.primitive;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data @AllArgsConstructor
+public class Size implements ISize {
+ private final double width;
+ private final double height;
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/DrawNothingRenderer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/DrawNothingRenderer.java
new file mode 100644
index 00000000..6bfb9af6
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/DrawNothingRenderer.java
@@ -0,0 +1,29 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.renderer;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+
+public class DrawNothingRenderer implements Renderer{
+ public static DrawNothingRenderer INSTANCE = new DrawNothingRenderer();
+ private DrawNothingRenderer() {}
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/OnlyChildrenRenderer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/OnlyChildrenRenderer.java
new file mode 100644
index 00000000..42d7e35d
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/OnlyChildrenRenderer.java
@@ -0,0 +1,51 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.renderer;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import net.minecraft.client.renderer.GlStateManager;
+
+public class OnlyChildrenRenderer implements Renderer {
+ public static OnlyChildrenRenderer INSTANCE = new OnlyChildrenRenderer();
+ protected OnlyChildrenRenderer() {}
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext renderingContext, DomElement buildContext) {
+ for (DomElement value : buildContext.getChildren()) {
+ Rect original = value.getRelativeBound();
+ GlStateManager.pushMatrix();
+ GlStateManager.translate(original.getX(), original.getY(), 0);
+
+ double absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth();
+ double absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight();
+
+ Rect elementABSBound = new Rect(
+ (buildContext.getAbsBounds().getX() + original.getX() * absXScale),
+ (buildContext.getAbsBounds().getY() + original.getY() * absYScale),
+ (original.getWidth() * absXScale),
+ (original.getHeight() * absYScale)
+ );
+ value.setAbsBounds(elementABSBound);
+
+ value.getRenderer().doRender(absMouseX, absMouseY,
+ relMouseX - original.getX(),
+ relMouseY - original.getY(), partialTicks,renderingContext, value);
+ GlStateManager.popMatrix();
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/Renderer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/Renderer.java
new file mode 100644
index 00000000..6f655916
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/Renderer.java
@@ -0,0 +1,34 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.renderer;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Position;
+
+public interface Renderer {
+ void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext);
+
+ /**
+ * Transform point so that it's in child's coordinate system.
+ * @param element
+ * @param pos
+ * @return
+ */
+ default Position transformPoint(DomElement element, Position pos) {return new Position(pos.getX() - element.getRelativeBound().getX(), pos.getY() - element.getRelativeBound().getY());}
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/RenderingContext.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/RenderingContext.java
new file mode 100644
index 00000000..7073e7be
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/RenderingContext.java
@@ -0,0 +1,138 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.renderer;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Position;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL33;
+import org.lwjgl.opengl.GL42;
+
+import java.awt.*;
+import java.util.Stack;
+
+/**
+ * Why are render methods here not static?
+ * Well, might put them all into one gigantic array and only do 1 call.
+ */
+public class RenderingContext {
+ public void drawRect(double left, double top, double right, double bottom, int color) {
+ double i;
+ if (left < right) {
+ i = left;
+ left = right;
+ right = i;
+ }
+
+ if (top < bottom) {
+ i = top;
+ top = bottom;
+ bottom = i;
+ }
+
+ float f = (float)(color >> 24 & 255) / 255.0F;
+ float g = (float)(color >> 16 & 255) / 255.0F;
+ float h = (float)(color >> 8 & 255) / 255.0F;
+ float j = (float)(color & 255) / 255.0F;
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldRenderer = tessellator.getWorldRenderer();
+ GlStateManager.enableBlend();
+ GlStateManager.disableTexture2D();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.color(g, h, j, f);
+ worldRenderer.begin(7, DefaultVertexFormats.POSITION);
+ worldRenderer.pos(left, bottom, 0.0).endVertex();
+ worldRenderer.pos(right, bottom, 0.0).endVertex();
+ worldRenderer.pos(right, top, 0.0).endVertex();
+ worldRenderer.pos(left, top, 0.0).endVertex();
+ tessellator.draw();
+ GlStateManager.enableTexture2D();
+ }
+ public void drawScaledCustomSizeModalRect(double x, double y, float u, float v, int uWidth, int vHeight, double width, double height, float tileWidth, float tileHeight) {
+ double f = 1.0F / tileWidth;
+ double g = 1.0F / tileHeight;
+ GlStateManager.enableTexture2D();
+ GlStateManager.enableBlend();
+ GlStateManager.color(1,1,1,1);
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldRenderer = tessellator.getWorldRenderer();
+ worldRenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldRenderer.pos(x, (y + height), 0.0).tex((u * f), ((v + vHeight) * g)).endVertex();
+ worldRenderer.pos((x + width), (y + height), 0.0).tex(((u + uWidth) * f), ((v + vHeight) * g)).endVertex();
+ worldRenderer.pos((x + width), y, 0.0).tex(((u + uWidth) * f), (v * g)).endVertex();
+ worldRenderer.pos(x, y, 0.0).tex((u * f), (v * g)).endVertex();
+ tessellator.draw();
+ }
+
+ public Stack<Rectangle> clips = new Stack<>();
+
+ public void pushClip(Rect absBounds, Size size, double x, double y, double width, double height) {
+ if (width < 0 || height < 0) throw new IllegalArgumentException("Clip width height less than 0");
+
+ Rectangle previousClip;
+ if (clips.size() == 0)
+ previousClip = new Rectangle(0,0,Integer.MAX_VALUE, Integer.MAX_VALUE);
+ else
+ previousClip = clips.peek();
+
+ double xScale = absBounds.getWidth() / size.getWidth();
+ double yScale = absBounds.getHeight() / size.getHeight();
+
+ int resWidth = (int) Math.ceil(width * xScale);
+ int resHeight = (int) Math.ceil(height * yScale);
+ int resX = (int) (absBounds.getX()+ x * xScale);
+ int resY = (int) (absBounds.getY() + y * yScale);
+
+
+ Rectangle newClip = new Rectangle(resX, Minecraft.getMinecraft().displayHeight - (resY+resHeight), resWidth, resHeight);
+ newClip = previousClip.intersection(newClip);
+
+ if (clips.size() == 0)
+ GL11.glEnable(GL11.GL_SCISSOR_TEST);
+
+ clips.push(newClip);
+
+ if (newClip.width <= 0 || newClip.height <= 0)
+ GL11.glColorMask(false, false ,false ,false);
+ else
+ GL11.glScissor(newClip.x, newClip.y, newClip.width, newClip.height);
+ }
+
+ public void popClip() {
+ Rectangle currentClip = clips.pop();
+
+ GL11.glColorMask(true, true ,true ,true);
+ if (clips.size() == 0)
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ else {
+ Rectangle newClip = clips.peek();
+ if (newClip.width <= 0 || newClip.height <= 0)
+ GL11.glColorMask(false, false ,false ,false);
+ else
+ GL11.glScissor(newClip.x, newClip.y, newClip.width, newClip.height);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/SingleChildRenderer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/SingleChildRenderer.java
new file mode 100644
index 00000000..48fbde81
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/renderer/SingleChildRenderer.java
@@ -0,0 +1,51 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.renderer;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Rect;
+import net.minecraft.client.renderer.GlStateManager;
+
+public class SingleChildRenderer implements Renderer {
+ public static final SingleChildRenderer INSTANCE = new SingleChildRenderer();
+ protected SingleChildRenderer() {}
+
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext renderingContext, DomElement buildContext) {
+ if (buildContext.getChildren().isEmpty()) return;
+ DomElement value = buildContext.getChildren().get(0);
+
+ Rect original = value.getRelativeBound();
+ GlStateManager.translate(original.getX(), original.getY(), 0);
+
+ double absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth();
+ double absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight();
+
+ Rect elementABSBound = new Rect(
+ (buildContext.getAbsBounds().getX() + original.getX() * absXScale),
+ (buildContext.getAbsBounds().getY() + original.getY() * absYScale),
+ (original.getWidth() * absXScale),
+ (original.getHeight() * absYScale)
+ );
+ value.setAbsBounds(elementABSBound);
+
+ value.getRenderer().doRender(absMouseX, absMouseY,
+ relMouseX - original.getX(),
+ relMouseY - original.getY(), partialTicks,renderingContext, value);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestPopup.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestPopup.java
new file mode 100644
index 00000000..977246f4
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestPopup.java
@@ -0,0 +1,38 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.view;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.PopupMgr;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedImportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.On;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.ResourceLocation;
+
+public class TestPopup extends AnnotatedImportOnlyWidget {
+ public TestPopup() {
+ super(new ResourceLocation("dungeonsguide:gui/testpopup.gui"));
+ }
+
+ @On(functionName = "close")
+ public void onClick() {
+ PopupMgr.getPopupMgr(getDomElement()).closePopup();
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestView.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestView.java
new file mode 100644
index 00000000..2325a3f8
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/view/TestView.java
@@ -0,0 +1,51 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2022 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.view;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.PopupMgr;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.AnnotatedImportOnlyWidget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.DomElementRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.On;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.ResourceLocation;
+
+public class TestView extends AnnotatedImportOnlyWidget {
+ public TestView() {
+ super(new ResourceLocation("dungeonsguide:gui/testview.gui"));
+ }
+
+ @Bind(variableName = "variable")
+ public final BindableAttribute<String> bindableAttribute = new BindableAttribute<>(String.class, "");
+
+ @Bind(variableName = "bDisable")
+ public final BindableAttribute<Boolean> bindableAttribute2 = new BindableAttribute<>(Boolean.class, false);
+
+ @Override
+ public void onMount() {
+ super.onMount();
+ bindableAttribute.setValue(Minecraft.getMinecraft().thePlayer.getName());
+ }
+
+ @On(functionName = "buttonClick")
+ public void onClick() {
+ PopupMgr.getPopupMgr(getDomElement()).openPopup(new TestPopup());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedExportOnlyWidget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedExportOnlyWidget.java
new file mode 100644
index 00000000..4363a15c
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedExportOnlyWidget.java
@@ -0,0 +1,86 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import org.apache.commons.lang3.reflect.FieldUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is for widgets using xml to describe their layout
+ */
+public abstract class AnnotatedExportOnlyWidget extends Widget implements ExportedWidget {
+
+ private Map<String, BindableAttribute> exportedAttributes = null;
+
+ @Export(attributeName = "ref")
+ public final BindableAttribute<DomElement> ref = new BindableAttribute<>(DomElement.class);
+
+ public DomElement createDomElement(DomElement parent) {
+ DomElement domElement = super.createDomElement(parent);
+ ref.setValue(domElement);
+ return domElement;
+ }
+
+ public abstract List<Widget> build(DomElement buildContext);
+
+ protected static Map<String, BindableAttribute> getExportedAttributes(Class clazz, Object inst) {
+ Map<String, BindableAttribute> attributeMap = new HashMap<>();
+ for (Field declaredField : FieldUtils.getAllFields(clazz)) {
+ if (declaredField.getAnnotation(Export.class) != null) {
+ Export export = declaredField.getAnnotation(Export.class);
+
+ if (declaredField.getType() != BindableAttribute.class) throw new IllegalStateException("Export Annotation must be applied on BindableAttribute field. : "+declaredField.getName());
+ if (!Modifier.isFinal(declaredField.getModifiers())) throw new IllegalStateException("Exported Bindable Attribute must be final : "+declaredField.getName());
+
+ try {
+ attributeMap.put(export.attributeName(), (BindableAttribute) declaredField.get(inst));
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return attributeMap;
+ }
+
+
+
+ private Map<String, BindableAttribute> getExportedAttributes() {
+ if (exportedAttributes == null)
+ exportedAttributes = getExportedAttributes(getClass(), this);
+ return exportedAttributes;
+ }
+
+ @Override
+ public <T> BindableAttribute<T> getExportedAttribute(String attributeName) {
+ return getExportedAttributes().get(attributeName);
+ }
+
+ public void onUnmount() {
+ for (BindableAttribute value : exportedAttributes.values()) {
+ value.unexportAll();
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedImportOnlyWidget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedImportOnlyWidget.java
new file mode 100644
index 00000000..a0db4293
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedImportOnlyWidget.java
@@ -0,0 +1,124 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Bind;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.On;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.Parser;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+import net.minecraft.util.ResourceLocation;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is for widgets using xml to describe their layout
+ */
+public abstract class AnnotatedImportOnlyWidget extends Widget implements ImportingWidget {
+ private Map<String, BindableAttribute> importedAttributes = null;
+ private Map<String, MethodHandle> invocationTargets = null;
+
+ private final ResourceLocation target;
+
+ public AnnotatedImportOnlyWidget(ResourceLocation resourceLocation) {
+ target = resourceLocation;
+ }
+
+ public final List<Widget> build(DomElement buildContext) {
+ try (Parser parser = DomElementRegistry.obtainParser(target)) {
+ ParserElement element = parser.getRootNode();
+ ParsedWidgetConverter converter = DomElementRegistry.obtainConverter(element.getNodename());
+ Widget w = converter.convert(this, element);
+ return Collections.singletonList(w);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected static Map<String, BindableAttribute> getImportedAttributes(Class clazz, Object inst) {
+ Map<String, BindableAttribute> attributes = new HashMap<>();
+ for (Field declaredField : FieldUtils.getAllFieldsList(clazz)) {
+ if (declaredField.getAnnotation(Bind.class) != null) {
+ Bind bind = declaredField.getAnnotation(Bind.class);
+
+ if (declaredField.getType() != BindableAttribute.class) throw new IllegalStateException("Bind Annotation must be applied on BindableAttribute field.");
+ if (!Modifier.isFinal(declaredField.getModifiers())) throw new IllegalStateException("Bound Bindable Attribute must be final ");
+
+ try {
+ attributes.put(bind.variableName(), (BindableAttribute) declaredField.get(inst));
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ return attributes;
+ }
+
+ protected static Map<String, MethodHandle> getInvocationTargets(Class clazz, Object inst) {
+ Map<String, MethodHandle> invocationTargets = new HashMap<>();
+ for (Method declaredMethod : clazz.getDeclaredMethods()) {
+ if (declaredMethod.getAnnotation(On.class) != null) {
+ On on = declaredMethod.getAnnotation(On.class);
+
+ try {
+ MethodHandle handle = MethodHandles.publicLookup().unreflect(declaredMethod);
+ invocationTargets.put(on.functionName(), handle);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return invocationTargets;
+ }
+
+ private Map<String, BindableAttribute> getImportedAttributes() {
+ if (importedAttributes == null)
+ importedAttributes = getImportedAttributes(getClass(), this);
+ return importedAttributes;
+ }
+
+ @Override
+ public <T> BindableAttribute<T> getBindTarget(String variableName, BindableAttribute<T> _) {
+ return getImportedAttributes().get(variableName);
+ }
+
+ private Map<String, MethodHandle> getInvocationTargets() {
+ if (invocationTargets == null)
+ invocationTargets = getInvocationTargets(getClass(), this);
+ return invocationTargets;
+ }
+ @Override
+ public MethodHandle getInvocationTarget(String functionName) {
+ return getInvocationTargets().get(functionName);
+ }
+}
+
+
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedWidget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedWidget.java
new file mode 100644
index 00000000..4171d4cf
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/AnnotatedWidget.java
@@ -0,0 +1,149 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Export;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Passthrough;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations.Passthroughs;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.Parser;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+import net.minecraft.util.ResourceLocation;
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandle;
+import java.util.*;
+
+/**
+ * This class is for widgets using xml to describe their layout
+ */
+public abstract class AnnotatedWidget extends Widget implements ImportingWidget, ExportedWidget {
+ private Map<String, BindableAttribute> importedAttributes = null;
+ private Map<String, BindableAttribute> exportedAttributes = null;
+ private Map<String, MethodHandle> invocationTargets = null;
+ @Export(attributeName = "ref")
+ public final BindableAttribute<DomElement> ref = new BindableAttribute<>(DomElement.class);
+
+
+ private final ResourceLocation target;
+ public AnnotatedWidget(ResourceLocation location) {
+ target = location;
+ }
+
+ public DomElement createDomElement(DomElement parent) {
+ DomElement domElement = super.createDomElement(parent);
+ ref.setValue(domElement);
+ return domElement;
+ }
+
+ public final List<Widget> build(DomElement buildContext) {
+ try (Parser parser = DomElementRegistry.obtainParser(target)) {
+ ParserElement element = parser.getRootNode();
+ if (element.getNodename().equals("multi")) {
+ List<Widget> widgets = new ArrayList<>();
+ for (ParserElement child : element.getChildren()) {
+ ParsedWidgetConverter converter = DomElementRegistry.obtainConverter(child.getNodename());
+ Widget w = converter.convert(this, child);
+ widgets.add(w);
+ }
+ return widgets;
+ } else {
+ ParsedWidgetConverter converter = DomElementRegistry.obtainConverter(element.getNodename());
+ Widget w = converter.convert(this, element);
+ return Collections.singletonList(w);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ protected static Pair<Map<String, BindableAttribute>, Map<String, BindableAttribute>> createPassthroughs(Class clazz) {
+ if (clazz.getAnnotation(Passthroughs.class) == null && clazz.getAnnotation(Passthrough.class) == null) return Pair.of(Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+ Map<String, BindableAttribute> attributeMap1 = new HashMap<>();
+ Map<String, BindableAttribute> attributeMap2 = new HashMap<>();
+ if (clazz.getAnnotation(Passthroughs.class) != null) {
+ Passthrough[] throughs = ((Passthroughs) clazz.getAnnotation(Passthroughs.class)).value();
+ for (Passthrough through : throughs) {
+ BindableAttribute attribute = new BindableAttribute(through.type());
+ attributeMap1.put(through.exportName(), attribute);
+ attributeMap2.put(through.bindName(), attribute);
+ }
+ }
+ if (clazz.getAnnotation(Passthrough.class) != null) {
+ Passthrough through = (Passthrough) clazz.getAnnotation(Passthrough.class);
+ BindableAttribute attribute = new BindableAttribute(through.type());
+ attributeMap1.put(through.exportName(), attribute);
+ attributeMap2.put(through.bindName(), attribute);
+ }
+ return Pair.of(attributeMap1, attributeMap2);
+ }
+
+
+ private Map<String, BindableAttribute> getExportedAttributes() {
+ if (exportedAttributes == null) {
+ exportedAttributes = AnnotatedExportOnlyWidget.getExportedAttributes(getClass(), this);
+ importedAttributes = AnnotatedImportOnlyWidget.getImportedAttributes(getClass(), this);
+
+ Pair<Map<String, BindableAttribute>, Map<String, BindableAttribute>> stuff = createPassthroughs(getClass());
+ exportedAttributes.putAll(stuff.getLeft());
+ importedAttributes.putAll(stuff.getRight());
+ }
+ return exportedAttributes;
+ }
+
+ @Override
+ public <T> BindableAttribute<T> getExportedAttribute(String attributeName) {
+ return getExportedAttributes().get(attributeName);
+ }
+
+ private Map<String, BindableAttribute> getImportedAttributes() {
+ if (importedAttributes == null) {
+ exportedAttributes = AnnotatedExportOnlyWidget.getExportedAttributes(getClass(), this);
+ importedAttributes = AnnotatedImportOnlyWidget.getImportedAttributes(getClass(), this);
+
+ Pair<Map<String, BindableAttribute>, Map<String, BindableAttribute>> stuff = createPassthroughs(getClass());
+ exportedAttributes.putAll(stuff.getLeft());
+ importedAttributes.putAll(stuff.getRight());
+ }
+ return importedAttributes;
+ }
+
+ @Override
+ public <T> BindableAttribute<T> getBindTarget(String variableName, BindableAttribute<T> _) {
+ return getImportedAttributes().get(variableName);
+ }
+ private Map<String, MethodHandle> getInvocationTargets() {
+ if (invocationTargets == null)
+ invocationTargets = AnnotatedImportOnlyWidget.getInvocationTargets(getClass(), this);
+ return invocationTargets;
+ }
+ @Override
+ public MethodHandle getInvocationTarget(String functionName) {
+ return getInvocationTargets().get(functionName);
+ }
+
+
+ public void onUnmount() {
+ for (BindableAttribute value : getExportedAttributes().values()) {
+ value.unexportAll();
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidget.java
new file mode 100644
index 00000000..7f7d7ba5
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidget.java
@@ -0,0 +1,96 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.Parser;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+import net.minecraft.util.ResourceLocation;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandle;
+import java.util.*;
+
+public class DelegatingWidget extends Widget implements ExportedWidget, ImportingWidget {
+
+ private final List<Widget> widgets;
+ public DelegatingWidget(ResourceLocation location) {
+
+
+ try (Parser parser = DomElementRegistry.obtainParser(location)) {
+ ParserElement element = parser.getRootNode();
+ if (!element.getNodename().equals("wrapper")) throw new IllegalArgumentException("Delegating widget root element Must be wrapper");
+ List<Widget> widgets = new ArrayList<>();
+ for (ParserElement child : element.getChildren()) {
+ ParsedWidgetConverter converter = DomElementRegistry.obtainConverter(child.getNodename());
+ Widget w = converter.convert(this, child);
+ widgets.add(w);
+ }
+ this.widgets = widgets;
+
+
+ for (String attribute : element.getAttributes()) {
+ getExportedAttribute(attribute).setValue(
+ StringConversions.convert(getExportedAttribute(attribute).getType(), element.getAttributeValue(attribute))
+ );
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ exportedAttributes.put("ref", ref);
+ }
+
+ private final Map<String, BindableAttribute> exportedAttributes = new HashMap<>();
+
+ private BindableAttribute<DomElement> ref = new BindableAttribute<>(DomElement.class);
+
+
+ @Override
+ public <T> BindableAttribute<T> getExportedAttribute(String attributeName) {
+ return exportedAttributes.get(attributeName);
+ }
+ @Override
+ public <T> BindableAttribute<T> getBindTarget(String variableName, BindableAttribute<T> from) {
+ if (exportedAttributes.containsKey(variableName))
+ return exportedAttributes.get(variableName);
+ BindableAttribute<T> newThing = new BindableAttribute<T>(from.getType());
+ exportedAttributes.put(variableName, newThing);
+ return newThing;
+ }
+
+ @Override
+ public MethodHandle getInvocationTarget(String functionName) {
+ throw new UnsupportedOperationException("lol");
+ }
+
+ @Override
+ public DomElement createDomElement(DomElement parent) {
+ DomElement domElement = super.createDomElement(parent);
+ ref.setValue(domElement);
+ return domElement;
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return widgets;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidgetConverter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidgetConverter.java
new file mode 100644
index 00000000..acdfcfeb
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DelegatingWidgetConverter.java
@@ -0,0 +1,54 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElementList;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.WidgetList;
+import net.minecraft.util.ResourceLocation;
+
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class DelegatingWidgetConverter<R extends Widget & ImportingWidget> extends PropByPropParsedWidgetConverter<DelegatingWidget, R> {
+ private final ResourceLocation resourceLocation;
+ public DelegatingWidgetConverter(ResourceLocation resourceLocation) {
+ this.resourceLocation = resourceLocation;
+ }
+
+ public DelegatingWidget instantiateWidget() {
+ return new DelegatingWidget(resourceLocation);
+ }
+
+ @Override
+ public BindableAttribute getExportedAttribute(DelegatingWidget widget, String attributeName) {
+ return widget.getExportedAttribute(attributeName);
+ }
+
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DomElementRegistry.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DomElementRegistry.java
new file mode 100644
index 00000000..e2c79b22
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/DomElementRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.*;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.image.ResourceImage;
+import kr.syeyoung.dungeonsguide.mod.guiv2.view.TestView;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.Parser;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserException;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.W3CBackedParser;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.resources.IResource;
+import net.minecraft.util.ResourceLocation;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DomElementRegistry {
+ private static final Map<String, ParsedWidgetConverter> converters = new HashMap<>();
+
+ public static void register(String xmlName, ParsedWidgetConverter converter) {
+ converters.put(xmlName, converter);
+ }
+
+ public static <T extends Widget, R extends Widget & ImportingWidget> ParsedWidgetConverter<T, R> obtainConverter(String name) {
+ return converters.get(name);
+ }
+
+ static {
+ register("stack", new ExportedWidgetConverter(Stack::new));
+ register("size", new ExportedWidgetConverter(SizedBox::new));
+ register("scaler", new ExportedWidgetConverter(Scaler::new));
+ register("row", new ExportedWidgetConverter(Row::new));
+ register("padding", new ExportedWidgetConverter(Padding::new));
+ register("col", new ExportedWidgetConverter(Column::new));
+ register("bgcolor", new ExportedWidgetConverter(Background::new));
+ register("align", new ExportedWidgetConverter(Align::new));
+ register("flexible", new ExportedWidgetConverter(Flexible::new));
+ register("line", new ExportedWidgetConverter(Line::new));
+ register("border", new ExportedWidgetConverter(Border::new));
+ register("Text", new ExportedWidgetConverter(Text::new));
+ register("slot", new ExportedWidgetConverter(Slot::new));
+ register("clip", new ExportedWidgetConverter(Clip::new));
+ register("measure", new ExportedWidgetConverter(Measure::new));
+ register("UnconstrainedBox", new ExportedWidgetConverter(UnconstrainedBox::new));
+ register("absXY", new ExportedWidgetConverter(AbsXY::new));
+ register("Placeholder", new ExportedWidgetConverter(Placeholder::new));
+ register("TextField", new ExportedWidgetConverter(TextField::new));
+ register("PopupManager", new ExportedWidgetConverter(PopupMgr::new));
+ register("AbstractButton", new ExportedWidgetConverter(Button::new));
+ register("ScrollablePanel", new ExportedWidgetConverter(ScrollablePanel::new));
+ register("AbstractScrollBar", new ExportedWidgetConverter(Scrollbar::new));
+ register("aspectRatio", new ExportedWidgetConverter(AspectRatioFitter::new));
+ register("BareResourceImage", new ExportedWidgetConverter(ResourceImage::new));
+ register("TestView", new ExportedWidgetConverter(TestView::new));
+
+ register("ColorButton", new DelegatingWidgetConverter(new ResourceLocation("dungeonsguide:gui/elements/simpleButton.gui")));
+ register("SimpleHorizontalScrollBar", new DelegatingWidgetConverter(new ResourceLocation("dungeonsguide:gui/elements/simpleHorizontalScrollBar.gui")));
+ register("SimpleVerticalScrollBar", new DelegatingWidgetConverter(new ResourceLocation("dungeonsguide:gui/elements/simpleVerticalScrollBar.gui")));
+ register("SlowList", new DelegatingWidgetConverter(new ResourceLocation("dungeonsguide:gui/elements/slowlist.gui")));
+
+ register("ResourceImage", new DelegatingWidgetConverter(new ResourceLocation("dungeonsguide:gui/elements/ratioResourceImage.gui")));
+
+ }
+
+ public static Parser obtainParser(ResourceLocation resourceLocation) {
+ try {
+ IResource iResource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
+ return new W3CBackedParser(iResource.getInputStream());
+ } catch (Exception e) {
+ throw new ParserException("An error occured while parsing "+resourceLocation, e);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidget.java
new file mode 100644
index 00000000..7e4efe7a
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidget.java
@@ -0,0 +1,25 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+
+public interface ExportedWidget {
+ <T> BindableAttribute<T> getExportedAttribute(String attributeName);
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidgetConverter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidgetConverter.java
new file mode 100644
index 00000000..aaaf2940
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ExportedWidgetConverter.java
@@ -0,0 +1,48 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+public class ExportedWidgetConverter<W extends Widget & ExportedWidget, R extends Widget & ImportingWidget> extends PropByPropParsedWidgetConverter<W, R> {
+ private Supplier<W> constructor;
+
+ public ExportedWidgetConverter(Supplier<W> constructor) {
+ this.constructor = Objects.requireNonNull(constructor);
+ }
+
+
+ public W instantiateWidget() {
+ return constructor.get();
+ }
+
+ public final BindableAttribute getExportedAttribute(W widget, String attributeName) {
+ return widget.getExportedAttribute(attributeName);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ImportingWidget.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ImportingWidget.java
new file mode 100644
index 00000000..63f3786e
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ImportingWidget.java
@@ -0,0 +1,29 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+
+import java.lang.invoke.MethodHandle;
+
+public interface ImportingWidget {
+ <T> BindableAttribute<T> getBindTarget(String variableName, BindableAttribute<T> from);
+
+ MethodHandle getInvocationTarget(String functionName);
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ParsedWidgetConverter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ParsedWidgetConverter.java
new file mode 100644
index 00000000..b2d813c7
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/ParsedWidgetConverter.java
@@ -0,0 +1,43 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+
+/**
+ * This class is for converting xml elements to widgets, to be used by XMLWidget
+ *
+ * type parameter R should be kept.
+ */
+public interface ParsedWidgetConverter<W extends Widget, R extends Widget & ImportingWidget> {
+ /**
+ * Converts xml element into a Widget
+ * xmlWidget is the widget that triggered reading the xml file
+ * Element ofc has all the props
+ * It's the converter's job to instantiate the widget and bind to appropriate variables in XMLWidget
+ * ^^ yes, I love dependency injection-like stuff
+ *
+ *
+ * @param rootWidget
+ * @param element
+ * @return
+ */
+ W convert(R rootWidget, ParserElement element);
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/PropByPropParsedWidgetConverter.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/PropByPropParsedWidgetConverter.java
new file mode 100644
index 00000000..c5290eea
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/PropByPropParsedWidgetConverter.java
@@ -0,0 +1,140 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.ParserElementList;
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.data.WidgetList;
+
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public abstract class PropByPropParsedWidgetConverter<W extends Widget, R extends Widget & ImportingWidget> implements ParsedWidgetConverter<W, R> {
+
+ public abstract W instantiateWidget();
+
+ public abstract BindableAttribute getExportedAttribute(W widget, String attributeName);
+
+ @Override
+ public W convert(R rootWidget, ParserElement element) {
+ W partial = instantiateWidget();
+
+ for (String attribute : element.getAttributes()) {
+ if (attribute.startsWith("bind:")) {
+ String name = attribute.substring(5);
+ String variable = element.getAttributeValue(attribute);
+
+ BindableAttribute exported = getExportedAttribute(partial, name);
+ if (exported == null) throw new IllegalStateException("No exported variable found named "+name+"!");
+ BindableAttribute bound = rootWidget.getBindTarget(variable, exported);
+ if (bound == null) throw new IllegalStateException("No bind target found for "+attribute+" for "+variable+"!");
+ exported.exportTo(bound);
+ } else if (attribute.startsWith("on:")) {
+ String name = attribute.substring(3);
+ String variable = element.getAttributeValue(attribute);
+
+ BindableAttribute exported = getExportedAttribute(partial, name);
+ if (exported == null) throw new IllegalStateException("No exported invocation target found named "+name+"!");
+ MethodHandle invocationTarget = rootWidget.getInvocationTarget(variable);
+ if (invocationTarget == null) throw new IllegalStateException("No invocationTarget target found for "+attribute+" for "+variable+"!");
+
+ // convert methodhandle to functional interface.
+ Class functionalInterface = exported.getType();
+ if (!functionalInterface.isInterface()) throw new IllegalArgumentException("Should be interface");
+ if (functionalInterface.getDeclaredMethods().length != 1)
+ throw new IllegalArgumentException("Should be functional interface");
+ Method m = functionalInterface.getDeclaredMethods()[0];
+
+ MethodType mt = MethodType.methodType(m.getReturnType(), m.getParameterTypes());
+ try {
+ Object obj = LambdaMetafactory.metafactory(MethodHandles.lookup(), m.getName(),
+ MethodType.methodType(functionalInterface, rootWidget.getClass()),
+ mt,
+ invocationTarget,
+ invocationTarget.type().dropParameterTypes(0, 1))
+ .getTarget()
+ .invoke(rootWidget);
+ exported.setValue(obj);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ // this should bind to methodhandle
+ } else if (attribute.equals("slot")) {
+ } else {
+ BindableAttribute bindableAttribute = getExportedAttribute(partial, attribute);
+ if (bindableAttribute == null) throw new IllegalStateException("No exported variable found named "+attribute+"!");
+ bindableAttribute.setValue(element.getConvertedAttributeValue(bindableAttribute.getType(), attribute));
+ }
+ }
+
+
+ Map<String, List<ParserElement>> children = new HashMap<>();
+ children.put("", new LinkedList<>());
+ for (ParserElement child : element.getChildren()) {
+ String slotName = child.getAttributeValue("slot");
+ if (slotName == null) slotName = "";
+
+ if (!children.containsKey(slotName))
+ children.put(slotName, new LinkedList<>());
+ children.get(slotName).add(child);
+ }
+
+ for (Map.Entry<String, List<ParserElement>> stringListEntry : children.entrySet()) {
+ BindableAttribute attribute = getExportedAttribute(partial, "$"+stringListEntry.getKey());
+ if (attribute == null) {
+ // ???
+ } else {
+ List<ParserElement> elements = stringListEntry.getValue();
+ if (attribute.getType() == ParserElement.class) {
+ if (elements.size() > 1) throw new IllegalArgumentException("More than 1 for single parser element: "+stringListEntry.getKey());
+ if (elements.size() == 1)
+ attribute.setValue(elements.get(0));
+ else
+ attribute.setValue(null);
+ } else if (attribute.getType() == Widget.class) {
+ if (elements.size() > 1) throw new IllegalArgumentException("More than 1 for single widget: "+stringListEntry.getKey());
+ if (elements.size() == 1)
+ attribute.setValue(DomElementRegistry.obtainConverter(elements.get(0).getNodename())
+ .convert(rootWidget, elements.get(0)));
+ else attribute.setValue(null);
+ } else if (attribute.getType() == ParserElementList.class) {
+ attribute.setValue(elements);
+ } else if (attribute.getType() == WidgetList.class) {
+ attribute.setValue(
+ elements.stream()
+ .map(a -> DomElementRegistry.obtainConverter(a.getNodename()).convert(rootWidget, a))
+ .collect(Collectors.toList()));
+ }
+ }
+ }
+
+ return partial;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/StringConversions.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/StringConversions.java
new file mode 100644
index 00000000..6e9138a1
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/StringConversions.java
@@ -0,0 +1,51 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml;
+
+import kr.syeyoung.dungeonsguide.mod.utils.RenderUtils;
+
+public final class StringConversions {
+ public static <T> T convert(Class<T> clazz, String val) {
+ if (clazz== Float.class) {
+ return (T) Float.valueOf(val);
+ } else if (clazz== Double.class) {
+ return (T) Double.valueOf(val);
+ } else if (clazz== Integer.class) {
+ if (val.startsWith("#"))
+ return (T) Integer.valueOf(Integer.parseUnsignedInt(val.substring(1), 16));
+ if (val.startsWith("0x"))
+ return (T) Integer.valueOf(Integer.parseUnsignedInt(val.substring(2), 16));
+ return (T) Integer.valueOf(val);
+ } else if (clazz== Short.class) {
+ if (val.startsWith("0x"))
+ return (T) Short.valueOf((short) Integer.parseUnsignedInt(val.substring(2), 16));
+ return (T) Short.valueOf(val);
+ } else if (clazz== String.class) {
+ return (T) val;
+ } else if (clazz.isEnum()) {
+ for (Object enumConstant : clazz.getEnumConstants()) {
+ if (val.equalsIgnoreCase(enumConstant.toString()))
+ return (T) enumConstant;
+ }
+ } else if (clazz== Boolean.class) {
+ return (T) Boolean.valueOf(val);
+ }
+ throw new UnsupportedOperationException("cant convert to "+clazz.getName());
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Bind.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Bind.java
new file mode 100644
index 00000000..7c70e8f2
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Bind.java
@@ -0,0 +1,30 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Bind {
+ String variableName();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Export.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Export.java
new file mode 100644
index 00000000..38ca3eb7
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Export.java
@@ -0,0 +1,30 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Export {
+ String attributeName();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/On.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/On.java
new file mode 100644
index 00000000..07f6eca0
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/On.java
@@ -0,0 +1,30 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface On {
+ String functionName();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthrough.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthrough.java
new file mode 100644
index 00000000..a0155f34
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthrough.java
@@ -0,0 +1,32 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Repeatable(Passthroughs.class)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Passthrough {
+
+ String exportName();
+ Class type();
+
+ String bindName();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthroughs.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthroughs.java
new file mode 100644
index 00000000..79e04fa8
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/annotations/Passthroughs.java
@@ -0,0 +1,27 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Passthroughs {
+ Passthrough[] value();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/Parser.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/Parser.java
new file mode 100644
index 00000000..0d700650
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/Parser.java
@@ -0,0 +1,25 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+import java.io.Closeable;
+
+public interface Parser extends AutoCloseable, Closeable {
+ ParserElement getRootNode();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElement.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElement.java
new file mode 100644
index 00000000..328164ad
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElement.java
@@ -0,0 +1,34 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+import java.util.List;
+import java.util.Set;
+
+public interface ParserElement {
+ String getNodename();
+
+ String getAttributeValue(String attribute);
+
+ Set<String> getAttributes();
+
+ <T> T getConvertedAttributeValue(Class<T> clazz, String attribute);
+
+ List<ParserElement> getChildren();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElementList.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElementList.java
new file mode 100644
index 00000000..059273c2
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserElementList.java
@@ -0,0 +1,26 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+
+import java.util.List;
+
+public interface ParserElementList extends List<ParserElement> {
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserException.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserException.java
new file mode 100644
index 00000000..fd97946b
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/ParserException.java
@@ -0,0 +1,38 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+public class ParserException extends RuntimeException {
+ public ParserException(Throwable cause) {super(cause);}
+
+ public ParserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public ParserException() {
+ }
+
+ public ParserException(String message) {
+ super(message);
+ }
+
+ public ParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParser.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParser.java
new file mode 100644
index 00000000..349218db
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParser.java
@@ -0,0 +1,70 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class W3CBackedParser implements Parser {
+ public static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ static {
+ factory.setIgnoringComments(true);
+ factory.setValidating(false);
+ factory.setNamespaceAware(false);
+ factory.setExpandEntityReferences(false);
+ factory.setCoalescing(false);
+ }
+
+
+ private final InputStream inputStream;
+ private final Element rootElement;
+
+ public W3CBackedParser(InputStream inputStream) throws IOException, SAXException, ParserConfigurationException {
+ this.inputStream = inputStream;
+
+ DocumentBuilder documentBuilder = factory.newDocumentBuilder();
+ Document document = documentBuilder.parse(inputStream);
+ NodeList nodeList = document.getChildNodes();
+ Element semiRoot = null;
+ for (int i = 0; i < nodeList.getLength(); i++)
+ if (nodeList.item(i) instanceof Element) {
+ semiRoot = (Element) nodeList.item(i);
+ break;
+ }
+ rootElement = semiRoot;
+ }
+
+ @Override
+ public ParserElement getRootNode() {
+ return new W3CBackedParserElement(rootElement);
+ }
+
+ @Override
+ public void close() throws IOException {
+ inputStream.close();
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParserElement.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParserElement.java
new file mode 100644
index 00000000..1bdc1750
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/W3CBackedParserElement.java
@@ -0,0 +1,72 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.xml.StringConversions;
+import org.w3c.dom.*;
+import scala.tools.nsc.doc.base.comment.Link;
+
+import java.util.*;
+
+public class W3CBackedParserElement implements ParserElement {
+ private final Element backingElement;
+
+ public W3CBackedParserElement(Element element) {
+ this.backingElement = element;
+ }
+
+ @Override
+ public String getNodename() {
+ return backingElement.getTagName();
+ }
+
+ @Override
+ public String getAttributeValue(String attribute) {
+ return backingElement.getAttribute(attribute);
+ }
+
+ @Override
+ public Set<String> getAttributes() {
+ NamedNodeMap nodeList = backingElement.getAttributes();
+ Set<String> list = new HashSet<>();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node n = nodeList.item(i);
+ if (!(n instanceof Attr)) continue;
+ list.add(((Attr) n).getName());
+ }
+ return list;
+ }
+
+ @Override
+ public <T> T getConvertedAttributeValue(Class<T> clazz, String attribute) {
+ return StringConversions.convert(clazz, getAttributeValue(attribute));
+ }
+
+ @Override
+ public List<ParserElement> getChildren() {
+ NodeList nodeList = backingElement.getChildNodes();
+ List<ParserElement> list = new LinkedList<>();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node n = nodeList.item(i);
+ if (!(n instanceof Element)) continue;
+ list.add(new W3CBackedParserElement((Element) n));
+ }
+ return list;
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/WidgetList.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/WidgetList.java
new file mode 100644
index 00000000..9e39d76b
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/xml/data/WidgetList.java
@@ -0,0 +1,26 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.xml.data;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+
+import java.util.List;
+
+public interface WidgetList extends List<Widget> {
+}