diff options
author | syeyoung <42869671+cyoung06@users.noreply.github.com> | 2023-01-13 13:11:13 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-13 13:11:13 +0900 |
commit | 87df9a914624b295e3e93db621ed2a8256eaf846 (patch) | |
tree | 538414a186e12b7ad1846462e8406c761a156a69 /mod/src/main/java/kr/syeyoung | |
parent | d3f5b4df809fd03919952285a1906652097f00e6 (diff) | |
download | Skyblock-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/syeyoung')
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> { +} |