diff options
author | syeyoung <cyoung06@naver.com> | 2023-02-04 02:24:07 +0900 |
---|---|---|
committer | syeyoung <cyoung06@naver.com> | 2023-02-04 02:24:07 +0900 |
commit | 29d6ebc4433318fb72d4587d7a67b522fd744619 (patch) | |
tree | 82b2f7cdd5e62ee38e666c5ec250cd8ea6f2b44e /loader/src/main/java | |
parent | f3410c50bdcc5c5ddbbdfa05ad539d178dfdbf84 (diff) | |
download | Skyblock-Dungeons-Guide-29d6ebc4433318fb72d4587d7a67b522fd744619.tar.gz Skyblock-Dungeons-Guide-29d6ebc4433318fb72d4587d7a67b522fd744619.tar.bz2 Skyblock-Dungeons-Guide-29d6ebc4433318fb72d4587d7a67b522fd744619.zip |
- add guiv2 to dg loader
Signed-off-by: syeyoung <cyoung06@naver.com>
Diffstat (limited to 'loader/src/main/java')
106 files changed, 9362 insertions, 0 deletions
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/BindableAttribute.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/BindableAttribute.java new file mode 100644 index 00000000..8c09f2cf --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/BindableAttribute.java @@ -0,0 +1,101 @@ +/* + * 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.launcher.guiv2; + +import lombok.Getter; + +import java.util.*; +import java.util.function.BiConsumer; + +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; + try { + if (!Objects.equals(t, old)) + for (BiConsumer<T, T> onUpdate : onUpdates) { + onUpdate.accept(old, value); + } + } finally { + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Context.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Context.java new file mode 100644 index 00000000..b59c2c2b --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java new file mode 100644 index 00000000..9487b21c --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java @@ -0,0 +1,255 @@ +/* + * 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.launcher.guiv2; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.Stack; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.NullLayouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Position; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.DrawNothingRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +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 CopyOnWriteArrayList<>(); + + @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 requestRelayoutParent() { + if (parent != null) + parent.requestRelayout(); + } + public void requestRelayout() { + if (layouter.canCutRequest() && size != null) { + layouter.layout(this, new ConstraintBox( + size.getWidth(), size.getWidth(), size.getHeight(), size.getHeight() + )); + return; + } + 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, boolean withinbound) { + if (absBounds == null) return false; + boolean isIn = absBounds.contains(absMouseX, absMouseY) && withinbound; + if (!isIn) { + if (wasMouseIn) widget.mouseExited(absMouseX, absMouseY, relMouseX0, relMouseY0); + } else { + if (!wasMouseIn) widget.mouseEntered(absMouseX, absMouseY, relMouseX0, relMouseY0); + } + wasMouseIn = isIn; + + for (DomElement childComponent : children) { + Position transformed = renderer.transformPoint(childComponent, new Position(relMouseX0, relMouseY0)); + + if (childComponent.mouseMoved0(absMouseX, absMouseY, transformed.x, transformed.getY(), isIn)) { + return true; + } + } + if (isIn) + return widget.mouseMoved(absMouseX, absMouseY, relMouseX0, relMouseY0); + return false; + } + + public void setCursor(EnumCursor enumCursor) { + parent.setCursor(enumCursor); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/ElementTreeWalkIterator.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/ElementTreeWalkIterator.java new file mode 100644 index 00000000..65352a14 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java new file mode 100644 index 00000000..03c60397 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java @@ -0,0 +1,303 @@ +/* + * 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.launcher.guiv2; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.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 org.lwjgl.LWJGLException; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import java.io.IOException; +import java.util.Stack; + +import static org.lwjgl.opengl.GL11.GL_GREATER; + +public class GuiScreenAdapter extends GuiScreen { + + @Getter + private RootDom view; + private boolean isOpen = false; + + private Stack<RootDom> domStack = new Stack<>(); + + public GuiScreenAdapter(Widget widget) { + view = new RootDom(widget); + view.getContext().CONTEXT.put("screenAdapter", this); + + try { + Mouse.setNativeCursor(GLCursors.getCursor(EnumCursor.DEFAULT)); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public void open(Widget newRoot) { + domStack.push(view); + view = new RootDom(newRoot); + view.getContext().CONTEXT.put("screenAdapter", this); + initGui(); + } + public void goBack() { + view = domStack.pop(); + view.getContext().CONTEXT.put("screenAdapter", this); + initGui(); + } + + public static GuiScreenAdapter getAdapter(DomElement domElement) { + return domElement.getContext().getValue(GuiScreenAdapter.class, "screenAdapter"); + } + + @Override + public void initGui() { + super.initGui(); + Keyboard.enableRepeatEvents(true); + isOpen = true; + try { + 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); + }catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + int i = Mouse.getEventX(); + int j = this.mc.displayHeight - Mouse.getEventY(); + + try { + if (view.isRelayoutRequested()) { + + view.setRelayoutRequested(false); + view.getLayouter().layout(view, new ConstraintBox( + Minecraft.getMinecraft().displayWidth, + Minecraft.getMinecraft().displayWidth, + Minecraft.getMinecraft().displayHeight, + Minecraft.getMinecraft().displayHeight + )); + } + + + 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(); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @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, true); + } 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 a temporary solution NEU Incompat. ? + super.handleInput(); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java new file mode 100644 index 00000000..d7c732f3 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java @@ -0,0 +1,71 @@ +/* + * 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.launcher.guiv2; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.SingleChildPassingLayouter; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Widget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Widget.java new file mode 100644 index 00000000..3e001f89 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Widget.java @@ -0,0 +1,93 @@ +/* + * 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.launcher.guiv2; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.SingleChildPassingLayouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; +import lombok.Getter; + +import java.util.List; + +public abstract class Widget { + + @Getter + private DomElement domElement = new DomElement(); + + + private boolean multiAtFirst = false; + public DomElement createDomElement(DomElement parent) { + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/AbsXY.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/AbsXY.java new file mode 100644 index 00000000..50ec1d08 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/AbsXY.java @@ -0,0 +1,80 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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); + if (child.getSize() == null) return; + 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(child, 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 double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), width); + } + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(child.getValue()); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Align.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Align.java new file mode 100644 index 00000000..b55fe961 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Align.java @@ -0,0 +1,77 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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 = buildContext.getChildren().get(0); + Size size = theOnly.getLayouter().layout(theOnly, new ConstraintBox( + 0, constraintBox.getMaxWidth(), 0, constraintBox.getMaxHeight() + )); + + double x = hAlign.getValue() == Alignment.START ? 0 : hAlign.getValue() == Alignment.CENTER ? (constraintBox.getMaxWidth() - size.getWidth())/2 : constraintBox.getMaxWidth() - size.getWidth(); + double y = vAlign.getValue() == Alignment.START ? 0 : vAlign.getValue() == Alignment.CENTER ? (constraintBox.getMaxHeight() - size.getHeight())/2 : constraintBox.getMaxHeight() - size.getHeight(); + + theOnly.setRelativeBound(new Rect(x, y, + size.getWidth(), size.getHeight() + )); + return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight()); + } + + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), width); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/AspectRatioFitter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/AspectRatioFitter.java new file mode 100644 index 00000000..982c1523 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/AspectRatioFitter.java @@ -0,0 +1,102 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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 mathematically?"); + } + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return height * this.width.getValue() / this.height.getValue(); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return width * this.height.getValue() / this.width.getValue(); + } + + @Override + public boolean canCutRequest() { + return Flexible.FlexFit.TIGHT == fit.getValue(); + } + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(widget.getValue()); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Background.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Background.java new file mode 100644 index 00000000..8fadc636 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Background.java @@ -0,0 +1,63 @@ +/* + * 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.launcher.guiv2.elements; + + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Border.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Border.java new file mode 100644 index 00000000..640a54c3 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Border.java @@ -0,0 +1,172 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.OnlyChildrenRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +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(bottom, 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 + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + 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 effHeight = height - top.getLayouter().getMaxIntrinsicHeight(top, 0) + - bottom.getLayouter().getMaxIntrinsicHeight(bottom, 0); + return content.getLayouter().getMaxIntrinsicWidth(content, effHeight) + + left.getLayouter().getMaxIntrinsicWidth(left, effHeight) + + right.getLayouter().getMaxIntrinsicWidth(right, effHeight); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + 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 effWidth = width - left.getLayouter().getMaxIntrinsicHeight(top, 0) + - right.getLayouter().getMaxIntrinsicHeight(bottom, 0); + return content.getLayouter().getMaxIntrinsicHeight(content, effWidth) + + top.getLayouter().getMaxIntrinsicHeight(top, width) + + bottom.getLayouter().getMaxIntrinsicHeight(bottom, width); + } + + @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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java new file mode 100644 index 00000000..73b0e21d --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java @@ -0,0 +1,128 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.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("dungeons_guide_loader: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) { + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java new file mode 100644 index 00000000..18738d42 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java @@ -0,0 +1,75 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.mod.shader.ShaderManager; +import kr.syeyoung.dungeonsguide.mod.shader.ShaderProgram; +import net.minecraft.client.Minecraft; +import org.lwjgl.opengl.GL20; + +import java.util.Collections; +import java.util.List; + +public class CircularRect 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 + protected Renderer createRenderer() { + return new BRender(); + } + + @Override + public List<Widget> build(DomElement buildContext) { + return child.getValue() == null ? Collections.EMPTY_LIST : Collections.singletonList(child.getValue()); + } + + public class BRender extends SingleChildRenderer { + @Override + public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext renderingContext, DomElement buildContext) { + Size size = buildContext.getSize(); + double radius = Math.min(size.getWidth(), size.getHeight()) / 2; + + ShaderProgram shaderProgram = ShaderManager.getShader("shaders/roundrect"); + shaderProgram.useShader(); + shaderProgram.uploadUniform("radius", (float)(radius * buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth())); + shaderProgram.uploadUniform("halfSize", (float) buildContext.getAbsBounds().getWidth()/2, (float) buildContext.getAbsBounds().getHeight()/2); + shaderProgram.uploadUniform("centerPos", + (float) (buildContext.getAbsBounds().getX()+buildContext.getAbsBounds().getWidth()/2), + Minecraft.getMinecraft().displayHeight - (float) (buildContext.getAbsBounds().getY() + buildContext.getAbsBounds().getHeight()/2)); + shaderProgram.uploadUniform("smoothness", 1.0f); + renderingContext.drawRect(0,0,buildContext.getSize().getWidth(), buildContext.getSize().getHeight(), color.getValue()); + GL20.glUseProgram(0); + super.doRender(absMouseX, absMouseY, relMouseX, relMouseY, partialTicks, renderingContext, buildContext); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Clip.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Clip.java new file mode 100644 index 00000000..6656d4df --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Clip.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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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; + if (buildContext.getSize().getWidth() <= 0 || buildContext.getSize().getHeight() <= 0) + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Column.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Column.java new file mode 100644 index 00000000..6d618886 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Column.java @@ -0,0 +1,255 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.OnlyChildrenRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.WidgetList; + +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 : buildContext.getChildren()) { + if (!(child.getWidget() instanceof Flexible)) { + Size requiredSize = child.getLayouter().layout(child, new ConstraintBox( + crossAxisAlignment == CrossAxisAlignment.STRETCH + ? constraints.getMaxWidth() : 0, constraints.getMaxWidth(), 0, Double.POSITIVE_INFINITY + )); + saved.put(child, requiredSize); + width = Math.max(width, requiredSize.getWidth()); + height += requiredSize.getHeight(); + } + } + + + + boolean flexFound = false; + int sumFlex = 0; + for (DomElement child : buildContext.getChildren()) { + if (child.getWidget() instanceof Flexible) { + sumFlex += Math.max(1, ((Flexible) child.getWidget()).flex.getValue()); + flexFound = true; + } + } + + if (flexFound && effheight == Double.POSITIVE_INFINITY) throw new IllegalStateException("Max height can not be infinite with flex elements"); + else if (effheight == Double.POSITIVE_INFINITY) effheight = height; + + if (flexFound) { + double remainingHeight = effheight - height; + if (remainingHeight < 0) remainingHeight = 0; + double heightPer = remainingHeight / sumFlex; + + for (DomElement child : buildContext.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() == Double.POSITIVE_INFINITY ? 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 / (buildContext.getChildren().size()-1); + } else { + starty = (effheight - height) / 2; + } + } else if (mainAxisAlignment == MainAxisAlignment.SPACE_EVENLY) { + double remaining = effheight - height; + if (remaining > 0) { + heightDelta = remaining / (buildContext.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 / buildContext.getChildren().size(); + starty = heightDelta / 2; + } else { + starty = (effheight - height / 2); + } + } + + for (DomElement child : buildContext.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 + ); + } + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + double height = 0; + double flex = 0; + double maxPer = 0; + for (DomElement child : buildContext.getChildren()) { + if (child.getWidget() instanceof Flexible) { + flex += ((Flexible) child.getWidget()).flex.getValue(); + maxPer = Double.max(maxPer, child.getLayouter().getMaxIntrinsicHeight(child, width) / + ((Flexible) child.getWidget()).flex.getValue()); + } else { + height += child.getLayouter().getMaxIntrinsicHeight(child, width); + } + } + return height + maxPer * flex; + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + double startingWidth = 0; + double prevWidth = 0; + double maxWidth = 0; + int circuitBreaker = 0; + do { + circuitBreaker++; + if (circuitBreaker > 100) { + try { + throw new RuntimeException("Caught in infinite loop welp"); + } catch (Exception e) { e.printStackTrace(); } + break; + } + prevWidth = startingWidth; + startingWidth = maxWidth; + double heightTaken = 0; + int sumFlex = 0; + for (DomElement child : buildContext.getChildren()) { + if (!(child.getWidget() instanceof Flexible)) { + heightTaken += child.getLayouter().getMaxIntrinsicHeight(child, startingWidth); + maxWidth = Double.max(maxWidth, child.getLayouter().getMaxIntrinsicWidth(child, 0)); + } else { + sumFlex += ((Flexible) child.getWidget()).flex.getValue(); + } + } + double leftOver = height - heightTaken; + if (leftOver <= 0) { + leftOver = 0; + if (prevWidth > 0) + maxWidth = prevWidth; + break; + } + if (sumFlex > 0) { + double per = leftOver / sumFlex; + if (height == 0) per = 0; + for (DomElement child : buildContext.getChildren()) { + if (child.getWidget() instanceof Flexible) { + maxWidth = Double.max(maxWidth, child.getLayouter().getMaxIntrinsicWidth(child, per * ((Flexible) child.getWidget()).flex.getValue())); + } + } + } + if (leftOver == 0 || sumFlex == 0) break; + } while (startingWidth != maxWidth); + return maxWidth; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CompatLayer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CompatLayer.java new file mode 100644 index 00000000..69ad5652 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CompatLayer.java @@ -0,0 +1,169 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.mod.gui.MPanel; +import kr.syeyoung.dungeonsguide.mod.gui.elements.MTooltip; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.AbsLocationPopup; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.PopupMgr; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import net.minecraft.client.Minecraft; +import org.lwjgl.opengl.GL11; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CompatLayer extends Widget implements Layouter, Renderer { + private MPanel panel; + private Rectangle force; + + public CompatLayer(MPanel panel) { + this(panel, null); + } + + List<AbsLocationPopup> tooltips = new ArrayList<>(); + public CompatLayer(MPanel panel, Rectangle force) { + this.panel = panel; + this.force = force; + panel.setParent(new MPanel() { + @Override + public void setCursor(EnumCursor enumCursor) { + getDomElement().setCursor(enumCursor); + } + + @Override + public int getTooltipsOpen() { + return 0; + } + + @Override + public void openTooltip(MTooltip mPanel) { + Rectangle bounds = mPanel.getBounds(); + AbsLocationPopup absLocationPopup = new AbsLocationPopup(bounds.getX(), bounds.getY(), new CompatLayer(mPanel, bounds), true); + PopupMgr.getPopupMgr(getDomElement()).openPopup(absLocationPopup, (a) -> {tooltips.remove(absLocationPopup);}); + tooltips.add(absLocationPopup); + } + }); + } + + @Override + public void onUnmount() { + super.onUnmount(); + tooltips.forEach(PopupMgr.getPopupMgr(getDomElement())::closePopup); + } + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.emptyList(); + } + + + + @Override + public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) { + getDomElement().obtainFocus(); + double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); + return panel.mouseClicked0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX,(int) relMouseY, mouseButton); + } + + @Override + public void mouseClickMove(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int clickedMouseButton, long timeSinceLastClick) { + double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); + panel.mouseClickMove0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX, (int)relMouseY, clickedMouseButton, timeSinceLastClick); + } + + @Override + public void mouseReleased(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int state) { + double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); + panel.mouseReleased0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX,(int) relMouseY, state); + } + + @Override + public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) { + double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); + panel.mouseMoved0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX0, (int)relMouseY0); + return true; + } + + @Override + public boolean mouseScrolled(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) { + double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); + panel.mouseScrolled0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX0, (int)relMouseY0, scrollAmount); + return true; + } + + @Override + public void keyPressed(char typedChar, int keyCode) { + panel.keyPressed0(typedChar, keyCode); + } + + @Override + public void keyReleased(char typedChar, int keyCode) { + panel.keyReleased0(typedChar, keyCode); + } + + @Override + public void keyHeld(char typedChar, int keyCode) { + panel.keyHeld0(typedChar, keyCode); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + Dimension dimension = force == null ? panel.getPreferredSize() : force.getSize(); + panel.resize((int) constraintBox.getMaxWidth(), (int) constraintBox.getMaxHeight()); + if (panel.getBounds().getWidth() != 0) dimension = panel.getSize(); + + panel.setBounds(new Rectangle(0,0, (int) dimension.getWidth(), (int) dimension.getHeight())); + return new Size(dimension.getWidth(), dimension.getHeight()); + } + + @Override + public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) { + double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); + + Rectangle originalRect = context.currentClip(); + Rectangle rectangle = originalRect == null ? null : originalRect.getBounds(); + boolean isNotNull = rectangle != null; + if (rectangle == null) rectangle = new Rectangle(0,0,Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight); + + rectangle.y = Minecraft.getMinecraft().displayHeight - rectangle.y - rectangle.height; + rectangle.width /= scale; + rectangle.height /= scale; + rectangle.x /= scale; + rectangle.y /= scale; + +// System.out.println(rectangle); + panel.render0(scale, new Point((int) (getDomElement().getAbsBounds().getX() / scale), (int) (getDomElement().getAbsBounds().getY() /scale)), + rectangle, (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX, (int)relMouseY, partialTicks); + + if (isNotNull) { + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(originalRect.x, originalRect.y, originalRect.width, originalRect.height); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ConstrainedBox.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ConstrainedBox.java new file mode 100644 index 00000000..d552c416 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ConstrainedBox.java @@ -0,0 +1,104 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class ConstrainedBox extends AnnotatedExportOnlyWidget implements Layouter { + + @Export(attributeName = "minWidth") + public final BindableAttribute<Double> minWidth = new BindableAttribute<>(Double.class, 0.0); + @Export(attributeName = "minHeight") + public final BindableAttribute<Double> minHeight = new BindableAttribute<>(Double.class, 0.0); + @Export(attributeName = "maxWidth") + public final BindableAttribute<Double> maxWidth = new BindableAttribute<>(Double.class, Double.POSITIVE_INFINITY); + @Export(attributeName = "maxHeight") + public final BindableAttribute<Double> maxHeight = 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 ConstrainedBox() { + minWidth.addOnUpdate((a, b) -> getDomElement().requestRelayoutParent()); + minHeight.addOnUpdate((a, b) -> getDomElement().requestRelayoutParent()); + maxWidth.addOnUpdate((a, b) -> getDomElement().requestRelayoutParent()); + maxHeight.addOnUpdate((a, b) -> getDomElement().requestRelayoutParent()); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + + double minWidth = Layouter.clamp(this.minWidth.getValue(), constraintBox.getMinWidth(), constraintBox.getMaxWidth()); + double minHeight = Layouter.clamp(this.minHeight.getValue(), constraintBox.getMinHeight(), constraintBox.getMaxHeight()); + + double maxWidth = Layouter.clamp(this.maxWidth.getValue(), constraintBox.getMinWidth(), constraintBox.getMaxWidth()); + double maxHeight = Layouter.clamp(this.maxHeight.getValue(), constraintBox.getMinHeight(), constraintBox.getMaxHeight()); + + if (getDomElement().getChildren().isEmpty()) { + return new Size(maxWidth == Double.POSITIVE_INFINITY ? 0 : maxWidth, + maxHeight == Double.POSITIVE_INFINITY ? 0 : maxHeight); + } + + DomElement child = getDomElement().getChildren().get(0); + Size dim = child.getLayouter().layout(child, new ConstraintBox( + minWidth, maxWidth, minHeight, maxHeight + )); // force size heh. + child.setRelativeBound(new Rect(0, 0, dim.getWidth(), dim.getHeight())); + return dim; + } + + @Override + public boolean canCutRequest() { + return Objects.equals(minWidth.getValue(), maxWidth.getValue()) + && Objects.equals(minHeight.getValue(), maxHeight.getValue()); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + DomElement child = buildContext.getChildren().get(0); + return Layouter.clamp(child.getLayouter().getMaxIntrinsicHeight(child, width), + minHeight.getValue() == Double.POSITIVE_INFINITY ? 0 : minHeight.getValue(), maxHeight.getValue()); + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + DomElement child = buildContext.getChildren().get(0); + return Layouter.clamp(child.getLayouter().getMaxIntrinsicWidth(child, height), + minWidth.getValue() == Double.POSITIVE_INFINITY ? 0 : minWidth.getValue(), maxWidth.getValue()); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Flexible.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Flexible.java new file mode 100644 index 00000000..27068dfd --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Flexible.java @@ -0,0 +1,92 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +// 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) -> Optional.ofNullable(getDomElement().getParent()).ifPresent(DomElement::requestRelayout)); + fit.addOnUpdate((a,b) -> Optional.ofNullable(getDomElement().getParent()).ifPresent(DomElement::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() == Double.POSITIVE_INFINITY ? 0 : constraintBox.getMaxWidth(), constraintBox.getMaxWidth() + , constraintBox.getMaxHeight() == Double.POSITIVE_INFINITY ? 0 : constraintBox.getMaxHeight(), constraintBox.getMaxHeight()) + : ConstraintBox.loose(constraintBox.getMaxWidth(), constraintBox.getMaxHeight()); + DomElement child = buildContext.getChildren().get(0); + + Size dim = child.getLayouter().layout(child, box); + + buildContext.getChildren().get(0).setRelativeBound(new Rect(0,0, dim.getWidth(),dim.getHeight())); + return dim; + } + + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), width); + } + + @Override + public boolean canCutRequest() { + return FlexFit.TIGHT == fit.getValue(); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/GlobalHUDScale.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/GlobalHUDScale.java new file mode 100644 index 00000000..263d624f --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/GlobalHUDScale.java @@ -0,0 +1,119 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Position; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; + +import java.util.Collections; +import java.util.List; + +public class GlobalHUDScale extends Widget implements Layouter, Renderer { + + private final Widget widget; + public GlobalHUDScale(Widget widget) { + this.widget = widget; + scale = getScale(); + } + + + private double getScale() { + return new ScaledResolution(Minecraft.getMinecraft()).getScaleFactor(); + } + + private double scale; + + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(widget); + } + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + this.scale = getScale(); + DomElement child = buildContext.getChildren().get(0); + Size dims = child.getLayouter().layout(child, new ConstraintBox( + (constraintBox.getMinWidth() / scale), + (constraintBox.getMaxWidth() / scale), + (constraintBox.getMinHeight() / scale), + (constraintBox.getMaxHeight() / scale) + )); + child.setRelativeBound(new Rect(0,0, + (dims.getWidth() * scale), + (dims.getHeight() * scale))); + child.setSize(new Size(dims.getWidth(), dims.getHeight())); + + return new Size(dims.getWidth() * scale, dims.getHeight() * scale); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + DomElement child = buildContext.getChildren().get(0); + return child.getLayouter().getMaxIntrinsicHeight(child, width / scale) * scale; + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + DomElement child = buildContext.getChildren().get(0); + return child.getLayouter().getMaxIntrinsicWidth(child, height / scale) * scale; + } + + @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, relY / scale); + } + + + @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, scale, 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, + (relMouseY - original.getY()) / scale, partialTicks, context, value); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/IntrinsicHeight.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/IntrinsicHeight.java new file mode 100644 index 00000000..e9a01cf0 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/IntrinsicHeight.java @@ -0,0 +1,65 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.List; + +public class IntrinsicHeight extends AnnotatedExportOnlyWidget implements Layouter { + @Export(attributeName = "_") + public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class); + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(widget.getValue()); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + DomElement elem = buildContext.getChildren().get(0); + double height = elem.getLayouter().getMaxIntrinsicHeight(elem, constraintBox.getMaxWidth() == Double.POSITIVE_INFINITY ? 0 : constraintBox.getMaxWidth()); + Size size = elem.getLayouter().layout(elem, new ConstraintBox( + constraintBox.getMinWidth(), constraintBox.getMaxWidth(), height, height + )); + elem.setRelativeBound(new Rect(0,0,size.getWidth(), size.getHeight())); + return size; + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + DomElement elem = buildContext.getChildren().get(0); + return elem.getLayouter().getMaxIntrinsicWidth(elem, height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + DomElement elem = buildContext.getChildren().get(0); + return elem.getLayouter().getMaxIntrinsicHeight(elem, width); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/IntrinsicWidth.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/IntrinsicWidth.java new file mode 100644 index 00000000..c731acb8 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/IntrinsicWidth.java @@ -0,0 +1,65 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.List; + +public class IntrinsicWidth extends AnnotatedExportOnlyWidget implements Layouter { + @Export(attributeName = "_") + public final BindableAttribute<Widget> widget = new BindableAttribute<>(Widget.class); + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(widget.getValue()); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + DomElement elem = buildContext.getChildren().get(0); + double width = elem.getLayouter().getMaxIntrinsicWidth(elem, constraintBox.getMaxHeight() == Double.POSITIVE_INFINITY ? 0 : constraintBox.getMaxHeight()); + Size size = elem.getLayouter().layout(elem, new ConstraintBox( + width, width, constraintBox.getMinHeight(), constraintBox.getMaxHeight() + )); + elem.setRelativeBound(new Rect(0,0,size.getWidth(), size.getHeight())); + return size; + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + DomElement elem = buildContext.getChildren().get(0); + return elem.getLayouter().getMaxIntrinsicWidth(elem, height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + DomElement elem = buildContext.getChildren().get(0); + return elem.getLayouter().getMaxIntrinsicHeight(elem, width); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Line.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Line.java new file mode 100644 index 00000000..b94a22aa --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Line.java @@ -0,0 +1,139 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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 double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return direction.getValue() == Orientation.HORIZONTAL ? 0 : this.thickness.getValue(); + } + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return direction.getValue() == Orientation.VERTICAL ? 0 : this.thickness.getValue(); + } + + @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((float) (thickness.getValue() * + Double.max( + buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight(), + buildContext.getAbsBounds().getWidth()/buildContext.getSize().getWidth() + )) + ); + + 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(); + GlStateManager.enableTexture2D(); + + if (pattern != null) { + GL11.glDisable(GL11.GL_LINE_STIPPLE); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Measure.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Measure.java new file mode 100644 index 00000000..bf7a037b --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Measure.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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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()); + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), width); + } + + @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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Navigator.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Navigator.java new file mode 100644 index 00000000..04c1c9b4 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Navigator.java @@ -0,0 +1,79 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.List; +import java.util.Stack; + +public class Navigator extends AnnotatedExportOnlyWidget { + public Stack<Widget> widgets = new Stack<>(); + + @Export(attributeName = "_") + public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class); + + public Widget current; + + public Navigator() { + child.addOnUpdate((old, a) -> { + if (current == null) current = a; + }); + } + + public void openPage(Widget widget) { + widgets.push(current); + getDomElement().removeElement(getDomElement().getChildren().get(0)); + getDomElement().addElement(widget.createDomElement(getDomElement())); + current = widget; + } + + public void goBack() { + getDomElement().removeElement(getDomElement().getChildren().get(0)); + Widget page; + if (widgets.isEmpty()) page = child.getValue(); + else page = widgets.pop(); + getDomElement().addElement(page.createDomElement(getDomElement())); + current = page; + } + + public static Navigator getNavigator(DomElement element) { + return element.getContext().getValue(Navigator.class, "navigator"); + } + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(current); + } + + @Override + public void onMount() { + getDomElement().getContext().CONTEXT.put("navigator", this); + } + + @Override + public void onUnmount() { + getDomElement().getContext().CONTEXT.remove("navigator"); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/NegativeStencil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/NegativeStencil.java new file mode 100644 index 00000000..8e2b268c --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/NegativeStencil.java @@ -0,0 +1,112 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import net.minecraft.client.renderer.GlStateManager; +import org.lwjgl.opengl.GL11; + +import java.util.Arrays; +import java.util.List; + +public class NegativeStencil extends AnnotatedExportOnlyWidget implements Renderer { + @Export(attributeName = "_") + public final BindableAttribute<Widget> children = new BindableAttribute<>(Widget.class); + @Export(attributeName = "_stencil") + public final BindableAttribute<Widget> stencil = new BindableAttribute<>(Widget.class); + + @Override + public List<Widget> build(DomElement buildContext) { + return Arrays.asList(children.getValue(), stencil.getValue()); + } + + @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) { + DomElement theThingToDraw = buildContext.getChildren().get(0); + DomElement stencil = buildContext.getChildren().get(1); + + GL11.glEnable(GL11.GL_STENCIL_TEST); + GL11.glClearStencil(0); + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + + GL11.glStencilMask(0xFF); + GL11.glColorMask(false, false, false, false); + GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF); + GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_REPLACE, GL11.GL_REPLACE); + + GlStateManager.pushMatrix(); + Rect original = stencil.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) + ); + stencil.setAbsBounds(elementABSBound); + + stencil.getRenderer().doRender(absMouseX, absMouseY, + relMouseX - original.getX(), + relMouseY - original.getY(), partialTicks,context, stencil); + GlStateManager.popMatrix(); + + + GL11.glStencilFunc(GL11.GL_EQUAL, 0, 0xFF); + GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + GL11.glColorMask(true, true, true, true); + + + original = theThingToDraw.getRelativeBound(); + GlStateManager.translate(original.getX(), original.getY(), 0); + + absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth(); + absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight(); + + elementABSBound = new Rect( + (buildContext.getAbsBounds().getX() + original.getX() * absXScale), + (buildContext.getAbsBounds().getY() + original.getY() * absYScale), + (original.getWidth() * absXScale), + (original.getHeight() * absYScale) + ); + theThingToDraw.setAbsBounds(elementABSBound); + + theThingToDraw.getRenderer().doRender(absMouseX, absMouseY, + relMouseX - original.getX(), + relMouseY - original.getY(), partialTicks,context, theThingToDraw); + + GL11.glDisable(GL11.GL_STENCIL_TEST); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Padding.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Padding.java new file mode 100644 index 00000000..6e2d2725 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Padding.java @@ -0,0 +1,97 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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 = buildContext.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); + } + + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return left.getValue() + right.getValue() + + (buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), + Math.max(0, height - top.getValue() - bottom.getValue()))); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return top.getValue() + bottom.getValue() + ( + buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), + Math.max(0, width - left.getValue() - right.getValue()))); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Placeholder.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Placeholder.java new file mode 100644 index 00000000..2670c0db --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.NullLayouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java new file mode 100644 index 00000000..217972e5 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java @@ -0,0 +1,73 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.mod.shader.ShaderManager; +import kr.syeyoung.dungeonsguide.mod.shader.ShaderProgram; +import net.minecraft.client.Minecraft; +import org.lwjgl.opengl.GL20; + +import java.util.Collections; +import java.util.List; + +public class RoundRect extends AnnotatedExportOnlyWidget { + @Export(attributeName = "radius") + public final BindableAttribute<Double> radius = new BindableAttribute<>(Double.class, 0.0); + + @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 + protected Renderer createRenderer() { + return new BRender(); + } + + @Override + public List<Widget> build(DomElement buildContext) { + return child.getValue() == null ? Collections.EMPTY_LIST : Collections.singletonList(child.getValue()); + } + + public class BRender extends SingleChildRenderer { + @Override + public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext renderingContext, DomElement buildContext) { + ShaderProgram shaderProgram = ShaderManager.getShader("shaders/roundrect"); + shaderProgram.useShader(); + shaderProgram.uploadUniform("radius", (float)(radius.getValue() * buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth())); + shaderProgram.uploadUniform("halfSize", (float) buildContext.getAbsBounds().getWidth()/2, (float) buildContext.getAbsBounds().getHeight()/2); + shaderProgram.uploadUniform("centerPos", + (float) (buildContext.getAbsBounds().getX()+buildContext.getAbsBounds().getWidth()/2), + Minecraft.getMinecraft().displayHeight - (float) (buildContext.getAbsBounds().getY() + buildContext.getAbsBounds().getHeight()/2)); + shaderProgram.uploadUniform("smoothness", 0.0f); + renderingContext.drawRect(0,0,buildContext.getSize().getWidth(), buildContext.getSize().getHeight(), color.getValue()); + GL20.glUseProgram(0); + super.doRender(absMouseX, absMouseY, relMouseX, relMouseY, partialTicks, renderingContext, buildContext); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Row.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Row.java new file mode 100644 index 00000000..05d89d75 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Row.java @@ -0,0 +1,263 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.OnlyChildrenRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.WidgetList; + +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 : buildContext.getChildren()) { + if (!(child.getWidget() instanceof Flexible)) { + Size requiredSize = child.getLayouter().layout(child, new ConstraintBox( + 0, Double.POSITIVE_INFINITY, + 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 : buildContext.getChildren()) { + if (child.getWidget() instanceof Flexible) { + sumFlex += Math.max(1, ((Flexible) child.getWidget()).flex.getValue()); + flexFound =true; + } + } + + if (flexFound && effwidth == Double.POSITIVE_INFINITY) throw new IllegalStateException("Max width can not be infinite with flex elements"); + else if (effwidth == Double.POSITIVE_INFINITY) effwidth = width; + + if (flexFound) { + double remainingWidth = effwidth - width; + if (remainingWidth < 0) remainingWidth = 0; + double widthPer = remainingWidth / sumFlex; + + for (DomElement child : buildContext.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() == Double.POSITIVE_INFINITY ? 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 / (buildContext.getChildren().size()-1); + } else { + startx = (effwidth - width) / 2; + } + } else if (mainAxisAlignment == MainAxisAlignment.SPACE_EVENLY) { + double remaining = effwidth - width; + if (remaining > 0) { + widthDelta = remaining / (buildContext.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 / buildContext.getChildren().size(); + startx = widthDelta / 2; + } else { + startx = (effwidth - width / 2); + } + } + + for (DomElement child : buildContext.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 + ); + } + + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + double prevHeight = 0; + double startingHeight = 0; + double maxHeight = 0; + int circuitBreaker = 0; + do { + circuitBreaker++; + if (circuitBreaker > 1000) { + try { + throw new RuntimeException("Caught in infinite loop welp"); + } catch (Exception e) { e.printStackTrace(); } + break; + } + prevHeight = startingHeight; + startingHeight = maxHeight; + maxHeight = 0; + double widthTaken = 0; + int sumFlex = 0; + for (DomElement child : buildContext.getChildren()) { + if (!(child.getWidget() instanceof Flexible)) { + widthTaken += child.getLayouter().getMaxIntrinsicWidth(child, startingHeight); + maxHeight = Double.max(maxHeight, child.getLayouter().getMaxIntrinsicHeight(child, 0)); + } else { + sumFlex += ((Flexible) child.getWidget()).flex.getValue(); + } + } + double leftOver = width - widthTaken; + if (leftOver <= 0) { + leftOver = 0; + if (prevHeight > 0) + maxHeight = prevHeight; + break; + } + if (sumFlex > 0) { + double per = leftOver / sumFlex; + if (width == 0) per = 0; + for (DomElement child : buildContext.getChildren()) { + if (child.getWidget() instanceof Flexible) { + maxHeight = Double.max(maxHeight, child.getLayouter().getMaxIntrinsicHeight(child, per * ((Flexible) child.getWidget()).flex.getValue())); + } + } + } + + if (leftOver == 0 || sumFlex == 0) break; + } while (startingHeight != maxHeight); + return maxHeight; + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + double width = 0; + double flex = 0; + double maxPer = 0; + for (DomElement child : buildContext.getChildren()) { + if (child.getWidget() instanceof Flexible) { + flex += ((Flexible) child.getWidget()).flex.getValue(); + maxPer = Double.max(maxPer, child.getLayouter().getMaxIntrinsicWidth(child, height) / + ((Flexible) child.getWidget()).flex.getValue()); + } else { + width += child.getLayouter().getMaxIntrinsicWidth(child, height); + } + } + return width + maxPer * flex; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Scaler.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Scaler.java new file mode 100644 index 00000000..bddc5bed --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Scaler.java @@ -0,0 +1,113 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Position; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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 = buildContext.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 double getMaxIntrinsicHeight(DomElement buildContext, double width) { + DomElement child = buildContext.getChildren().get(0); + return child.getLayouter().getMaxIntrinsicHeight(child, width / scale.getValue()) * scale.getValue(); + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + DomElement child = buildContext.getChildren().get(0); + return child.getLayouter().getMaxIntrinsicWidth(child, height / scale.getValue()) * 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ScrollablePanel.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ScrollablePanel.java new file mode 100644 index 00000000..523a880b --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.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("dungeons_guide_loader:gui/elements/scrollablePanel.gui")); + + contentSize.addOnUpdate((old, neu) -> { + contentWidth.setValue(Math.max(0, neu.getWidth() - viewportWidth.getValue())); + contentHeight.setValue(Math.max(0, neu.getHeight() - viewportHeight.getValue())); + }); + + viewportSize.addOnUpdate((old, neu) -> { + viewportWidth.setValue(neu.getWidth()); + viewportHeight.setValue(neu.getHeight()); + contentWidth.setValue(Math.max(0, contentSize.getValue().getWidth() - viewportWidth.getValue())); + contentHeight.setValue(Math.max(0, 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Scrollbar.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Scrollbar.java new file mode 100644 index 00000000..89549107 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Scrollbar.java @@ -0,0 +1,165 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.OnlyChildrenRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.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("dungeons_guide_loader: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; + } + + @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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/SelectiveContainer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/SelectiveContainer.java new file mode 100644 index 00000000..603c527c --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/SelectiveContainer.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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SelectiveContainer extends AnnotatedExportOnlyWidget { + @Export(attributeName = "visible") + public final BindableAttribute<String> visibleChild = new BindableAttribute<>(String.class); + + private final Map<String, BindableAttribute<Widget>> widgetMap = new HashMap<>(); + + public SelectiveContainer() { + visibleChild.addOnUpdate((old, neu) -> { + if (getDomElement().getChildren().size() > 0) + getDomElement().removeElement(getDomElement().getChildren().get(0)); + if (getDomElement().getParent() != null) { + Widget widget = widgetMap.get("_"+visibleChild.getValue()).getValue(); + getDomElement().addElement(widget.createDomElement(getDomElement())); + } + }); + } + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(widgetMap.get("_"+visibleChild.getValue()).getValue()); + } + + @Override + public <T> BindableAttribute<T> getExportedAttribute(String attributeName) { + BindableAttribute<T> bindableAttribute = super.getExportedAttribute(attributeName); + if (bindableAttribute == null && attributeName.startsWith("_")) { + BindableAttribute attribute = new BindableAttribute(Widget.class); + widgetMap.put(attributeName, attribute); + return attribute; + } + return bindableAttribute; + } + + @Override + public void onUnmount() { + for (BindableAttribute<Widget> value : widgetMap.values()) { + value.unexportAll(); + } + super.onUnmount(); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Slot.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Slot.java new file mode 100644 index 00000000..d239946b --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Slot.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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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); + + public Slot() { + replacement.addOnUpdate(this::update); + original.addOnUpdate(this::update); + } + + private void update(Widget widget, Widget widget1) { + if (this.getDomElement().getParent() == null) return; + getDomElement().removeElement(getDomElement().getChildren().get(0)); + + DomElement domElement = null; + if (replacement.getValue() != null) domElement = replacement.getValue().createDomElement(getDomElement()); + else if (original.getValue() != null) domElement = original.getValue().createDomElement(getDomElement()); + + if (domElement != null) + getDomElement().addElement(domElement); + } + + + @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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Stack.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Stack.java new file mode 100644 index 00000000..abcf4364 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Stack.java @@ -0,0 +1,113 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.WidgetList; +import net.minecraft.client.renderer.GlStateManager; + +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(); + if (original == null) return; + 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); + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + double max = 0; + for (DomElement child : buildContext.getChildren()) { + max = Double.max(max, child.getLayouter().getMaxIntrinsicWidth(child, height)); + } + return max; + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + double max = 0; + for (DomElement child : buildContext.getChildren()) { + max = Double.max(max, child.getLayouter().getMaxIntrinsicHeight(child, width)); + } + return max; + } + } + + + @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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Stencil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Stencil.java new file mode 100644 index 00000000..d9e45b24 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Stencil.java @@ -0,0 +1,112 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import net.minecraft.client.renderer.GlStateManager; +import org.lwjgl.opengl.GL11; + +import java.util.Arrays; +import java.util.List; + +public class Stencil extends AnnotatedExportOnlyWidget implements Renderer { + @Export(attributeName = "_") + public final BindableAttribute<Widget> children = new BindableAttribute<>(Widget.class); + @Export(attributeName = "_stencil") + public final BindableAttribute<Widget> stencil = new BindableAttribute<>(Widget.class); + + @Override + public List<Widget> build(DomElement buildContext) { + return Arrays.asList(children.getValue(), stencil.getValue()); + } + + @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) { + DomElement theThingToDraw = buildContext.getChildren().get(0); + DomElement stencil = buildContext.getChildren().get(1); + + GL11.glEnable(GL11.GL_STENCIL_TEST); + GL11.glClearStencil(0); + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + + GL11.glStencilMask(0xFF); + GL11.glColorMask(false, false, false, false); + GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF); + GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_REPLACE, GL11.GL_REPLACE); + + GlStateManager.pushMatrix(); + Rect original = stencil.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) + ); + stencil.setAbsBounds(elementABSBound); + + stencil.getRenderer().doRender(absMouseX, absMouseY, + relMouseX - original.getX(), + relMouseY - original.getY(), partialTicks,context, stencil); + GlStateManager.popMatrix(); + + + GL11.glStencilFunc(GL11.GL_EQUAL, 1, 0xFF); + GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + GL11.glColorMask(true, true, true, true); + + + original = theThingToDraw.getRelativeBound(); + GlStateManager.translate(original.getX(), original.getY(), 0); + + absXScale = buildContext.getAbsBounds().getWidth() / buildContext.getSize().getWidth(); + absYScale = buildContext.getAbsBounds().getHeight() / buildContext.getSize().getHeight(); + + elementABSBound = new Rect( + (buildContext.getAbsBounds().getX() + original.getX() * absXScale), + (buildContext.getAbsBounds().getY() + original.getY() * absYScale), + (original.getWidth() * absXScale), + (original.getHeight() * absYScale) + ); + theThingToDraw.setAbsBounds(elementABSBound); + + theThingToDraw.getRenderer().doRender(absMouseX, absMouseY, + relMouseX - original.getX(), + relMouseY - original.getY(), partialTicks,context, theThingToDraw); + + GL11.glDisable(GL11.GL_STENCIL_TEST); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Text.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Text.java new file mode 100644 index 00000000..761c7a0d --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Text.java @@ -0,0 +1,205 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.BreakWord; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.RichText; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.TextSpan; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.SingleColorShader; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.ParentDelegatingTextStyle; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public class Text extends AnnotatedExportOnlyWidget { + @Export(attributeName = "text") + public final BindableAttribute<String> text = new BindableAttribute<>(String.class, ""); + @Export(attributeName = "size") + public final BindableAttribute<Double> size = new BindableAttribute<>(Double.class, 8.0); + + private final ParentDelegatingTextStyle textStyle = ParentDelegatingTextStyle.ofDefault(); + private final RichText richText = new RichText(new TextSpan(textStyle, ""), BreakWord.WORD, false,RichText.TextAlign.LEFT); + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(richText); + } + 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) -> { + updateText(); + }); + wordBreak.addOnUpdate((a,b) -> { + richText.setBreakWord(b == WordBreak.WORD ? BreakWord.WORD : BreakWord.ALL); + }); + lineSpacing.addOnUpdate((a,b) -> { + updateText(); + }); + size.addOnUpdate((a,b) -> { + updateText(); + }); + textAlign.addOnUpdate((a,b) -> { + richText.setAlign(b == TextAlign.LEFT ? RichText.TextAlign.LEFT : b == TextAlign.CENTER ? RichText.TextAlign.CENTER : RichText.TextAlign.RIGHT); + }); + color.addOnUpdate((a,b) -> { + updateText(); + }); + } + + public Text(String text, int color, TextAlign align, WordBreak wordBreak, double lineSpacing, double size) { + this(); + this.text.setValue(text); + this.color.setValue(color); + this.textAlign.setValue(align); + this.wordBreak.setValue(wordBreak); + this.lineSpacing.setValue(lineSpacing); + this.size.setValue(size); + } + + private void updateText() { + textStyle.textShader = new SingleColorShader(color.getValue()); + textStyle.underlineShader = new SingleColorShader(color.getValue()); + textStyle.strikeThroughShader = new SingleColorShader(color.getValue()); + textStyle.topAscent = lineSpacing.getValue() - 1; + textStyle.size = size.getValue(); + TextSpan textSpan = new TextSpan(textStyle, ""); + for (TextSpan textSpan2 : parseText(text.getValue())) { + textSpan.addChild(textSpan2); + } + richText.setRootSpan(textSpan); + } + + private static final int[] colorCode = new int[32]; + static { + for(int i = 0; i < 32; ++i) { + int j = (i >> 3 & 1) * 85; + int k = (i >> 2 & 1) * 170 + j; + int l = (i >> 1 & 1) * 170 + j; + int i1 = (i & 1) * 170 + j; + if (i == 6) { + k += 85; + } + + if (i >= 16) { + k /= 4; + l /= 4; + i1 /= 4; + } + + colorCode[i] = (k & 255) << 16 | (l & 255) << 8 | i1 & 255; + } + } + private List<TextSpan> parseText(String text) { + Boolean boldStyle = null, strikethroughStyle = null, underlineStyle = null, italicStyle = null; + Integer textColor = null; + List<TextSpan> spans = new ArrayList<>(); + + StringBuilder stringBuilder = new StringBuilder(); + for(int i = 0; i < text.length(); ++i) { + char c0 = text.charAt(i); + if (c0 == 167 && i + 1 < text.length()) { + if (!stringBuilder.toString().isEmpty()) { + spans.add(new TextSpan( + new ParentDelegatingTextStyle() + .setTextShader(textColor == null ? null : new SingleColorShader(textColor | 0xFF000000)) + .setBold(boldStyle) + .setStrikeThroughShader(textColor == null ? null : new SingleColorShader(textColor | 0xFF000000)) + .setStrikeThrough(strikethroughStyle) + .setUnderlineShader(textColor == null ? null : new SingleColorShader(textColor | 0xFF000000)) + .setUnderline(underlineStyle) + .setItalics(italicStyle), + stringBuilder.toString() + )); + stringBuilder = new StringBuilder(); + } + + + int i1 = "0123456789abcdefklmnor".indexOf(text.toLowerCase(Locale.ENGLISH).charAt(i + 1)); + if (i1 < 16) { + boldStyle = false; + strikethroughStyle = false; + underlineStyle = false; + italicStyle = false; + if (i1 < 0) { + i1 = 15; + } + textColor = colorCode[i1]; + } else if (i1 == 16) { + } else if (i1 == 17) { + boldStyle = true; + } else if (i1 == 18) { + strikethroughStyle = true; + } else if (i1 == 19) { + underlineStyle = true; + } else if (i1 == 20) { + italicStyle = true; + } else { + boldStyle = false; + strikethroughStyle = false; + underlineStyle = false; + italicStyle = false; + textColor = null; + } + + ++i; + } else { + stringBuilder.append(c0); + } + } + + if (!stringBuilder.toString().isEmpty()) { + spans.add(new TextSpan( + new ParentDelegatingTextStyle() + .setTextShader(textColor == null ? null : new SingleColorShader(textColor | 0xFF000000)) + .setBold(boldStyle) + .setStrikeThroughShader(textColor == null ? null : new SingleColorShader(textColor | 0xFF000000)) + .setStrikeThrough(strikethroughStyle) + .setUnderlineShader(textColor == null ? null : new SingleColorShader(textColor | 0xFF000000)) + .setUnderline(underlineStyle) + .setItalics(italicStyle), + stringBuilder.toString() + )); + } + return spans; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java new file mode 100644 index 00000000..5cc977c8 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java @@ -0,0 +1,445 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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 double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return 0; + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return 15; + } + + @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) { + if (selectionEnd > text.length()) selectionEnd = text.length(); + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java new file mode 100644 index 00000000..8b4e7256 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java @@ -0,0 +1,104 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.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 = "_on", bindName = "wgtOn", type = Widget.class) +@Passthrough(exportName = "_off", bindName = "wgtOff", type = Widget.class) +@Passthrough(exportName = "_hoverOn", bindName = "wgtHoverOn", type = Widget.class) +@Passthrough(exportName = "_hoverOff", bindName = "wgtHoverOff", type = Widget.class) +public class ToggleButton extends AnnotatedWidget implements Renderer { + + @Bind(variableName = "refOn") + public final BindableAttribute<DomElement> on = new BindableAttribute<DomElement>(DomElement.class); + @Bind(variableName = "refOff") + public final BindableAttribute<DomElement> off = new BindableAttribute<DomElement>(DomElement.class); + @Bind(variableName = "refHoverOn") + public final BindableAttribute<DomElement> hoverOn = new BindableAttribute<DomElement>(DomElement.class); + @Bind(variableName = "refHoverOff") + public final BindableAttribute<DomElement> hoverOff = new BindableAttribute<DomElement>(DomElement.class); + + + @Export(attributeName = "enabled") + public final BindableAttribute<Boolean> enabled = new BindableAttribute<>(Boolean.class); + + public ToggleButton() { + super(new ResourceLocation("dungeons_guide_loader:gui/elements/toggleButton.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 (enabled.getValue()) { + value = isHover ? hoverOn.getValue() : on.getValue(); + } else { + value = isHover ? hoverOff.getValue() : off.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); + } + + @Override + public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) { + getDomElement().obtainFocus(); + enabled.setValue(!enabled.getValue()); + return true; + } + + @Override + public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) { + getDomElement().setCursor(EnumCursor.POINTING_HAND); + return true; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/UnconstrainedBox.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/UnconstrainedBox.java new file mode 100644 index 00000000..b8ef7fdd --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/UnconstrainedBox.java @@ -0,0 +1,71 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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()); + } + + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), width); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Wrap.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Wrap.java new file mode 100644 index 00000000..7105d498 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Wrap.java @@ -0,0 +1,130 @@ +/* + * 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.launcher.guiv2.elements; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.OnlyChildrenRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.WidgetList; + +import java.util.List; + +public class Wrap extends AnnotatedExportOnlyWidget implements Layouter { + + @Export(attributeName = "minimumWidth") + public final BindableAttribute<Double> minimumWidth = new BindableAttribute<>(Double.class); + @Export(attributeName = "gap") + public final BindableAttribute<Double> gap = new BindableAttribute<>(Double.class); + + @Export(attributeName = "_") + public final BindableAttribute<WidgetList> widgetListBindableAttribute = new BindableAttribute<>(WidgetList.class); + + @Override + public List<Widget> build(DomElement buildContext) { + return widgetListBindableAttribute.getValue(); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + List<DomElement> elements = buildContext.getChildren(); + if (elements.isEmpty()) return new Size(constraintBox.getMinWidth(),constraintBox.getMinHeight()); + int itemPerRow = (int) ((constraintBox.getMaxWidth()+gap.getValue()) / (minimumWidth.getValue() + gap.getValue())); + if (itemPerRow == 0) itemPerRow = 1; + if (itemPerRow == Integer.MAX_VALUE) itemPerRow = elements.size(); + int rows = (int) Math.ceil((double)elements.size() / itemPerRow); + + double maxWidth = (constraintBox.getMaxWidth() + gap.getValue()) / itemPerRow - gap.getValue(); + double maxHeight = (constraintBox.getMaxHeight() + gap.getValue()) / rows - gap.getValue(); + double h = 0; + for (int y = 0; y < rows; y++) { + double maxH = 0; + for (int x = 0; x < itemPerRow; x++) { + if (elements.size() <= y * itemPerRow + x) continue; + DomElement element = elements.get(y * itemPerRow + x); + double height = element.getLayouter().getMaxIntrinsicHeight(element, maxWidth); + if (height > maxH) maxH = height; + } + for (int x = 0; x < itemPerRow; x++) { + if (elements.size() <= y * itemPerRow + x) continue; + DomElement element = elements.get(y * itemPerRow + x); + Size size = element.getLayouter().layout(element, new ConstraintBox( + maxWidth, maxWidth, maxH, maxH + )); + element.setRelativeBound(new Rect( + x * (maxWidth + gap.getValue()), + h, + size.getWidth(), + size.getHeight() + )); + } + + h += maxH + gap.getValue(); + } + + return new Size(constraintBox.getMaxWidth(), Layouter.clamp(h - gap.getValue(), 0, constraintBox.getMaxHeight())); + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().stream() + .map(a -> a.getLayouter().getMaxIntrinsicWidth(a, height) + gap.getValue()) + .reduce(Double::sum) + .orElse(gap.getValue()) - gap.getValue(); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + List<DomElement> elements = buildContext.getChildren(); + if (elements.isEmpty()) return 0; + int itemPerRow = (int) ((width+gap.getValue()) / (minimumWidth.getValue() + gap.getValue())); + if (itemPerRow == 0) itemPerRow = 1; + if (itemPerRow == Integer.MAX_VALUE) itemPerRow = elements.size(); + int rows = (int) Math.ceil((double)elements.size() / itemPerRow); + double maxWidth = (width + gap.getValue()) / itemPerRow - gap.getValue(); + + double sum = 0; + for (int y = 0; y < rows; y++) { + double maxH = 0; + for (int x = 0; x < itemPerRow; x++) { + if (elements.size() <= y * itemPerRow + x) continue; + DomElement element = elements.get(y * itemPerRow + x); + double h = element.getLayouter().getMaxIntrinsicHeight(element, maxWidth); + if (h > maxH) maxH = h; + } + sum += maxH + gap.getValue(); + } + sum -= gap.getValue(); + if (sum < 0) sum = 0; + + return sum; + } + + @Override + protected Renderer createRenderer() { + return OnlyChildrenRenderer.INSTANCE; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/Image.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/Image.java new file mode 100644 index 00000000..422a5a41 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/Image.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.launcher.guiv2.elements.image; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import lombok.AllArgsConstructor; +import net.minecraft.client.Minecraft; +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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java new file mode 100644 index 00000000..e5ceec72 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java @@ -0,0 +1,171 @@ +/* + * 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.launcher.guiv2.elements.image; + + +import kr.syeyoung.dungeonsguide.mod.DungeonsGuide; +import lombok.Data; +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.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageInputStream; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +@Data +public class ImageTexture { + private String url; + private BufferedImage image; + private DynamicTexture previewTexture; + private ResourceLocation resourceLocation; + + private int width; + private int height; + private int frames; + private int size; + + private long startedPlayingAt = -1; + + private int delayTime; + + public void buildGLThings() { + previewTexture = new DynamicTexture(image); + resourceLocation = Minecraft.getMinecraft().getTextureManager().getDynamicTextureLocation("dgurl/"+url, previewTexture); + } + + public ImageTexture(String url) throws IOException { + this.url = url; + + URL urlObj = new URL(url); + HttpURLConnection huc = (HttpURLConnection) urlObj.openConnection(); + huc.addRequestProperty("User-Agent", "DungeonsGuideMod (dungeons.guide, 1.0)"); + ImageInputStream imageInputStream = ImageIO.createImageInputStream(huc.getInputStream()); + Iterator<ImageReader> readers = ImageIO.getImageReaders(imageInputStream); + if(!readers.hasNext()) throw new IOException("No image reader what" + url); + ImageReader reader = readers.next(); + reader.setInput(imageInputStream); + frames = reader.getNumImages(true); + BufferedImage dummyFrame = reader.read(0); + width = dummyFrame.getWidth(); height = dummyFrame.getHeight(); + + IIOMetadata imageMetaData = reader.getImageMetadata(0); + String metaFormatName = imageMetaData.getNativeMetadataFormatName(); + + IIOMetadataNode root = (IIOMetadataNode)imageMetaData.getAsTree(metaFormatName); + + IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension"); + + try { + delayTime = Integer.parseInt(graphicsControlExtensionNode.getAttribute("delayTime")) * 10; + } catch (Exception e) { + delayTime = 1000; + } + + image = new BufferedImage(width, height * frames, dummyFrame.getType()); + Graphics2D graphics2D = image.createGraphics(); + + for (int i = 0; i < frames; i++) { + BufferedImage bufferedImage = reader.read(i); + graphics2D.drawImage(bufferedImage, 0, i*height, null); + } + reader.dispose(); imageInputStream.close(); huc.disconnect(); + } + + + private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) { + int nNodes = rootNode.getLength(); + for (int i = 0; i < nNodes; i++) { + if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)== 0) { + return((IIOMetadataNode) rootNode.item(i)); + } + } + IIOMetadataNode node = new IIOMetadataNode(nodeName); + rootNode.appendChild(node); + return(node); + } + + public void drawFrame(double x, double y, double width, double height) { + if (getResourceLocation() == null) + buildGLThings(); + if (startedPlayingAt == -1) startedPlayingAt = System.currentTimeMillis(); + + int frame = (int) (((System.currentTimeMillis() - startedPlayingAt) / delayTime) % frames); + + TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); + textureManager.bindTexture(getResourceLocation()); + + GlStateManager.color(1, 1, 1, 1.0F); + + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(x, (y + height), 0.0D) + .tex(0,((frame+1) * height)/ ((double)frames * height)).endVertex(); + worldrenderer.pos((x + width), (y + height), 0.0D) + .tex(1, ((frame+1) * height)/ ((double)frames * height)).endVertex(); + worldrenderer.pos((x + width), y, 0.0D) + .tex(1,(frame * height)/ ((double)frames * height)).endVertex(); + worldrenderer.pos(x, y, 0.0D) + .tex(0, (frame * height) / ((double)frames * height)).endVertex(); + tessellator.draw(); + } + + public static final ExecutorService executorService = Executors.newFixedThreadPool(3, DungeonsGuide.THREAD_FACTORY); + public static final Map<String, ImageTexture> imageMap = new HashMap<>(); + public static final Logger logger = LogManager.getLogger("DG-ImageLoader"); + public static void loadImage(String url, Consumer<ImageTexture> callback) { + if (imageMap.containsKey(url)) { + callback.accept(imageMap.get(url)); + return; + } + if (url.isEmpty()) callback.accept(null); + executorService.submit(() -> { + try { + ImageTexture imageTexture = new ImageTexture(url); + imageMap.put(url, imageTexture); + callback.accept(imageTexture); + } catch (Exception e) { + callback.accept(null); + logger.log(Level.WARN, "An error occurred while loading image from: "+url, e); + } + }); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ResourceImage.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ResourceImage.java new file mode 100644 index 00000000..f2797691 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.elements.image; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/URLImage.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/URLImage.java new file mode 100644 index 00000000..f47964dd --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/URLImage.java @@ -0,0 +1,87 @@ +/* + * 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.launcher.guiv2.elements.image; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; + +import java.util.Collections; +import java.util.List; + +public class URLImage extends AnnotatedExportOnlyWidget implements Renderer, Layouter { + @Export(attributeName="url") + public final BindableAttribute<String> url = new BindableAttribute<String >(String.class); + + private ImageTexture imageTexture; + + public URLImage() { + url.addOnUpdate((prev, neu) -> { + ImageTexture.loadImage(neu, (texture) -> { + this.imageTexture = texture; + if (getDomElement() != null) + getDomElement().requestRelayout(); + }); + }); + } + + @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) { + if (imageTexture == null) return; + imageTexture.drawFrame(0,0,buildContext.getSize().getWidth(), buildContext.getSize().getHeight()); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + if (imageTexture == null) return new Size(constraintBox.getMinWidth(), constraintBox.getMinHeight()); + + double heightIfWidthFit = constraintBox.getMaxWidth() * imageTexture.getHeight() / imageTexture.getWidth(); + double widthIfHeightFit = constraintBox.getMaxHeight() * imageTexture.getWidth() / imageTexture.getHeight(); + + if (heightIfWidthFit <= constraintBox.getMaxHeight()) { + return new Size(constraintBox.getMaxWidth(), Layouter.clamp(heightIfWidthFit, constraintBox.getMinHeight(), constraintBox.getMaxHeight())); + } else if (widthIfHeightFit <= constraintBox.getMaxWidth()){ + return new Size(Layouter.clamp(widthIfHeightFit, constraintBox.getMinWidth(), constraintBox.getMaxWidth()), constraintBox.getMaxHeight()); + } else { + throw new IllegalStateException("How is this possible mathematically?"); + } + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return imageTexture == null ? 0 : height * imageTexture.getWidth() / imageTexture.getHeight(); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return imageTexture == null ? 0 : width * imageTexture.getHeight() / imageTexture.getWidth(); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/AbsLocationPopup.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/AbsLocationPopup.java new file mode 100644 index 00000000..f2cb80d0 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/AbsLocationPopup.java @@ -0,0 +1,98 @@ +/* + * 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.launcher.guiv2.elements.popups; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedImportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import net.minecraft.util.ResourceLocation; + +public class AbsLocationPopup extends AnnotatedImportOnlyWidget { + @Bind(variableName = "x") + public final BindableAttribute<Double> x = new BindableAttribute<>(Double.class); + @Bind(variableName = "y") + public final BindableAttribute<Double> y = new BindableAttribute<>(Double.class);; + @Bind(variableName = "ref") + public final BindableAttribute<DomElement> ref = new BindableAttribute<>(DomElement.class); + @Bind(variableName = "child") + public final BindableAttribute<Widget> child = new BindableAttribute<>(Widget.class); + @Bind(variableName = "size") + public final BindableAttribute<Size> size = new BindableAttribute<>(Size.class, new Size(0,0)); + + public final BindableAttribute<Double> absX = new BindableAttribute<>(Double.class); + public final BindableAttribute<Double> absY = new BindableAttribute<>(Double.class); + public boolean autoclose = false; + public AbsLocationPopup(double x, double y, Widget child, boolean autoclose) { + super(new ResourceLocation("dungeons_guide_loader:gui/elements/locationedPopup.gui")); + absX.setValue(x); + absY.setValue(y); + absX.addOnUpdate(this::updatePos); + absY.addOnUpdate(this::updatePos); + size.addOnUpdate((old, neu) -> updatePos(0,0)); + this.child.setValue(child); + this.autoclose = autoclose; + } + public AbsLocationPopup(BindableAttribute<Double> x, BindableAttribute<Double> y, Widget child, boolean autoclose) { + super(new ResourceLocation("dungeons_guide_loader:gui/elements/locationedPopup.gui")); + x.exportTo(this.absX); + y.exportTo(this.absY); + absX.addOnUpdate(this::updatePos); + absY.addOnUpdate(this::updatePos); + size.addOnUpdate((old, neu) -> updatePos(0,0)); + this.child.setValue(child); + this.autoclose = autoclose; + } + + @Override + public void onMount() { + updatePos(0,0); + } + + public void updatePos(double old, double neu) { + PopupMgr popupMgr = PopupMgr.getPopupMgr(getDomElement()); + Rect rect = popupMgr.getDomElement().getAbsBounds(); + Size rel = popupMgr.getDomElement().getSize(); + + Size size = this.size.getValue(); + this.x.setValue( + Layouter.clamp((absX.getValue() - rect.getX()) * rel.getWidth() / rect.getWidth(), 0, rel.getWidth()-size.getWidth()) + ); + this.y.setValue( + Layouter.clamp((absY.getValue() - rect.getY()) * rel.getHeight() / rect.getHeight(), 0, rel.getHeight() - size.getHeight()) + ); + } + + @Override + public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) { + if (!ref.getValue().getAbsBounds().contains(absMouseX, absMouseY) && autoclose) { + PopupMgr.getPopupMgr(getDomElement()).closePopup(this,null); + } + return false; + } + + @Override + public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) { + return true; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/MinecraftTooltip.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/MinecraftTooltip.java new file mode 100644 index 00000000..158feaf9 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/MinecraftTooltip.java @@ -0,0 +1,58 @@ +/* + * 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.launcher.guiv2.elements.popups; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.client.config.GuiUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MinecraftTooltip extends Widget implements Renderer, Layouter { + public List<String> tooltip = new ArrayList<>(); + + public void setTooltip(List<String> tooltip) { + this.tooltip = tooltip; + } + + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.emptyList(); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + return new Size(constraintBox.getMaxWidth(), constraintBox.getMaxHeight()); + } + + @Override + public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) { + GuiUtils.drawHoveringText(tooltip,0,0, + (int) buildContext.getRelativeBound().getWidth(), + (int) buildContext.getRelativeBound().getHeight(), -1, Minecraft.getMinecraft().fontRendererObj); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/MouseTooltip.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/MouseTooltip.java new file mode 100644 index 00000000..5aec45cc --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/MouseTooltip.java @@ -0,0 +1,56 @@ +/* + * 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.launcher.guiv2.elements.popups; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import org.lwjgl.input.Mouse; + +import java.util.Collections; +import java.util.List; + +public class MouseTooltip extends Widget { + private final AbsLocationPopup absLocationPopup; + + private final BindableAttribute<Double> x = new BindableAttribute<>(Double.class, (double)Mouse.getX()); + private final BindableAttribute<Double> y = new BindableAttribute<>(Double.class, (double)Mouse.getY()); + + public MouseTooltip(Widget content) { + absLocationPopup = new AbsLocationPopup(x,y, content, false); + } + @Override + public List<Widget> build(DomElement buildContext) { + return Collections.singletonList(absLocationPopup); + } + + @Override + public void onUnmount() { + super.onUnmount(); + x.unexportAll(); + y.unexportAll(); + } + + @Override + public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) { + x.setValue((double) absMouseX); + y.setValue((double) absMouseY); + return false; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/PopupMgr.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/PopupMgr.java new file mode 100644 index 00000000..31b06af8 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/popups/PopupMgr.java @@ -0,0 +1,92 @@ +/* + * 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.launcher.guiv2.elements.popups; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import net.minecraft.util.ResourceLocation; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class PopupMgr extends AnnotatedWidget { + public PopupMgr() { + super(new ResourceLocation("dungeons_guide_loader: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); + getDomElement().getContext().CONTEXT.put("popupWidth", this.getDomElement().getRelativeBound().getWidth()); + getDomElement().getContext().CONTEXT.put("popupHeight", this.getDomElement().getRelativeBound().getHeight()); + } + + @Override + public void onUnmount() { + domElementBindableAttribute.getValue().getChildren().clear(); + super.onUnmount(); + } + + public static PopupMgr getPopupMgr(DomElement buildContext) { + return buildContext.getContext().getValue(PopupMgr.class, "popup"); + } + + + private Map<DomElement, Consumer<Object>> callbacks = new HashMap<>(); + public void openPopup(Widget element, Consumer<Object> callback) { + DomElement domElement = element.createDomElement(getDomElement()); + domElementBindableAttribute.getValue().addElementFirst(domElement); + callbacks.put(domElement, callback); + } + + public void closePopup(Object value) { + DomElement target = domElementBindableAttribute.getValue().getChildren().get(0); + domElementBindableAttribute.getValue().removeElement( + target + ); + + Consumer<Object> callback = callbacks.remove(target); + if (callback != null) + callback.accept(value); + } + + public void closePopup(Widget widget, Object value) { + domElementBindableAttribute.getValue().removeElement(widget.getDomElement()); + + Consumer<Object> callback = callbacks.get(widget.getDomElement()); + if (callback != null) + callback.accept(value); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/BreakWord.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/BreakWord.java new file mode 100644 index 00000000..05b0a33e --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/BreakWord.java @@ -0,0 +1,23 @@ +/* + * 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.launcher.guiv2.elements.richtext; + +public enum BreakWord { + ALL, WORD +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/BrokenWordData.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/BrokenWordData.java new file mode 100644 index 00000000..a5f41646 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/BrokenWordData.java @@ -0,0 +1,31 @@ +/* + * 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.launcher.guiv2.elements.richtext; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data @AllArgsConstructor +public class BrokenWordData { + private FlatTextSpan first; + private FlatTextSpan second; + private boolean broken; + + private double firstWidth; +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/FlatTextSpan.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/FlatTextSpan.java new file mode 100644 index 00000000..f0a93ed7 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/FlatTextSpan.java @@ -0,0 +1,202 @@ +/* + * 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.launcher.guiv2.elements.richtext; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.ITextStyle; + +public class FlatTextSpan { + public final ITextStyle textStyle; + + public final char[] value; + + public FlatTextSpan(ITextStyle textStyle, char[] value) { + this.textStyle = textStyle; + this.value = value; + } + + public double getWidth() { + double sum =0; + for (char c : value) { + sum += textStyle.getFontRenderer().getWidth(c, textStyle); + } + return sum; + } + public double getHeight() { + return (1 + textStyle.getTopAscent() + textStyle.getBottomAscent()) * textStyle.getSize(); + } + public double getBaseline() { + return textStyle.getSize() * (textStyle.getFontRenderer().getBaselineHeight(textStyle) + textStyle.getTopAscent()); + } + + public BrokenWordData breakWord(double remainingWidth, double nextLineWidth, BreakWord breakWord) { + if (breakWord == BreakWord.WORD) { + // prefer to break on words + double scaledRemainingWidth = remainingWidth; + double scaledNextLineWidth = nextLineWidth; + + double totalWidth = 0; + double wordWidth = 0; + double charWidth = 0; + int wordStart = 0; + int potentialBreak = -1; + + int endIdx = value.length; + boolean lineBroken = false; + for (int i = 0; i < value.length; i++) { + char character = value[i]; + charWidth = textStyle.getFontRenderer().getWidth(character, textStyle); + + totalWidth += charWidth; + wordWidth += charWidth; + + if (character == ' ' || character == '\n') { + if (potentialBreak == -1) { + } else if (scaledNextLineWidth < wordWidth) { + // Break at potential, word is greater than next line + endIdx = potentialBreak; + lineBroken = true; + break; + } else { + // Delegate to next line. + endIdx = wordStart; + lineBroken = true; + break; + } + + // Force break. + if (character == '\n') { + endIdx = i+1; + lineBroken = true; + break; + } + + // Since adding space exceeded remaining width, break without adding space. + if (totalWidth > scaledRemainingWidth) { + endIdx = i; + lineBroken = true; + break; + } + + // reset states + wordStart = i + 1; + potentialBreak = -1; + wordWidth = 0; + } + + if (totalWidth > scaledRemainingWidth && potentialBreak == -1) { + potentialBreak = i; + } + } + if (potentialBreak == -1) { + } else if (scaledNextLineWidth < wordWidth) { + // Break at potential, word is greater than next line + endIdx = potentialBreak; + lineBroken = true; + } else { + // Delegate to next line. + endIdx = wordStart; + lineBroken = true; + } + + char[] first = new char[endIdx]; + System.arraycopy(value, 0, first, 0, endIdx); + + char[] second = null; + if (lineBroken) { + int startRealWord = -1; + for (int i = endIdx; i< value.length; i++) { + if (value[i] == ' ') continue; + startRealWord = i; + break; + } + if (startRealWord != -1) { + second = new char[value.length - startRealWord]; + System.arraycopy(value, startRealWord, second, 0, second.length); + } + } + + FlatTextSpan flatTextSpan = new FlatTextSpan(textStyle, first); + FlatTextSpan secondSpan = null; + if (second != null) + secondSpan = new FlatTextSpan(textStyle, second); + + + double resultingWidth = 0; + for (char c : first) { + resultingWidth += textStyle.getFontRenderer().getWidth(c, textStyle); + } + + return new BrokenWordData(flatTextSpan, secondSpan, lineBroken, resultingWidth); + } else { + // break anywhere + double scaledRemainingWidth = remainingWidth; + + double totalWidth = 0; + double effectiveWidth = 0; + boolean lineBroken =false; + + int endIdx = value.length; + for (int i = 0; i < value.length; i++) { + char character = value[i]; + double charWidth = textStyle.getFontRenderer().getWidth(character, textStyle); + + totalWidth += charWidth; + + if (character == '\n') { + // Force break. + endIdx = i + 1; + lineBroken = true; + break; + } + + if (totalWidth > scaledRemainingWidth) { + // break here. + endIdx = i; + lineBroken = true; + break; + } + effectiveWidth += charWidth; + } + + char[] first = new char[endIdx]; + System.arraycopy(value, 0, first, 0, endIdx); + + char[] second = null; + if (lineBroken) { + int startRealWord = -1; + for (int i = endIdx; i< value.length; i++) { + if (value[i] == ' ') continue; + startRealWord = i; + break; + } + if (startRealWord != -1) { + second = new char[value.length - startRealWord]; + System.arraycopy(value, startRealWord, second, 0, second.length); + } + } + + FlatTextSpan flatTextSpan = new FlatTextSpan(textStyle, first); + FlatTextSpan secondSpan = null; + if (second != null) + secondSpan = new FlatTextSpan(textStyle, second); + + return new BrokenWordData(flatTextSpan, secondSpan, lineBroken, effectiveWidth); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/RichLine.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/RichLine.java new file mode 100644 index 00000000..0e84d01c --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/RichLine.java @@ -0,0 +1,33 @@ +/* + * 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.launcher.guiv2.elements.richtext; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@Data @AllArgsConstructor +public class RichLine { + private List<FlatTextSpan> lineElements; + + private double width; + private double height; + private double baseline; +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/RichText.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/RichText.java new file mode 100644 index 00000000..18e6ea00 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/RichText.java @@ -0,0 +1,224 @@ +/* + * 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.launcher.guiv2.elements.richtext; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; +import lombok.Setter; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class RichText extends Widget implements Layouter, Renderer { + private TextSpan rootSpan; + private BreakWord breakWord; + private boolean takeAllSpace = false; + @Setter + private TextAlign align; + + + private List<RichLine> richLines = new LinkedList<>(); + public static enum TextAlign { + LEFT, CENTER, RIGHT + } + + public RichText(TextSpan textSpan, BreakWord breakWord, boolean takeAllSpace, TextAlign align) { + this.rootSpan = textSpan; + this.breakWord = breakWord; + this.takeAllSpace = takeAllSpace; + this.align = align; + } + + public void setRootSpan(TextSpan rootSpan) { + this.rootSpan = rootSpan; + this.getDomElement().requestRelayout(); + } + + public void setBreakWord(BreakWord breakWord) { + this.breakWord = breakWord; + this.getDomElement().requestRelayout(); + } + + public void setTakeAllSpace(boolean takeAllSpace) { + this.takeAllSpace = takeAllSpace; + this.getDomElement().requestRelayout(); + } + + @Override + public Size layout(DomElement buildContext, ConstraintBox constraintBox) { + LinkedList<FlatTextSpan> flatTextSpans = new LinkedList<>(); + rootSpan.flattenTextSpan(flatTextSpans::add); + + LinkedList<RichLine> lines = new LinkedList<>(); + LinkedList<FlatTextSpan> line = new LinkedList<>(); + double remaining = constraintBox.getMaxWidth(); + double usedUp = 0; + double maxHeight = 0; + double maxBaseline = 0; + + double sumHeight = 0; + double maxWidth = 0; + + while (!flatTextSpans.isEmpty()) { + FlatTextSpan first = flatTextSpans.pollFirst(); + + BrokenWordData brokenWordData = first.breakWord(remaining, constraintBox.getMaxWidth(), breakWord); + remaining -= brokenWordData.getFirstWidth(); + usedUp += brokenWordData.getFirstWidth(); + line.add(brokenWordData.getFirst()); + + if (brokenWordData.getFirst().value.length == 0 && first.value.length != 0 && remaining == constraintBox.getMaxWidth()) { + try { + throw new IllegalStateException("Can not fit stuff into this"); + } catch (Exception e) { + e.printStackTrace(); + break; + } + } + + maxHeight = Math.max(maxHeight, first.getHeight()); + maxBaseline = Math.max(maxBaseline, first.getBaseline()); + + if (brokenWordData.getSecond() != null) { + flatTextSpans.addFirst(brokenWordData.getSecond()); + } + + if (brokenWordData.isBroken()) { + lines.add(new RichLine(line, usedUp, maxHeight, maxBaseline)); + line = new LinkedList<>(); + maxWidth = Math.max(maxWidth, usedUp); + sumHeight += maxHeight; + + remaining = constraintBox.getMaxWidth(); + usedUp = 0; + maxHeight = 0; + maxBaseline = 0; + } + } + if (!line.isEmpty()) { + lines.add(new RichLine(line, usedUp, maxHeight, maxBaseline)); + maxWidth = Math.max(maxWidth, usedUp); + sumHeight += maxHeight; + } + + richLines = lines; + + return new Size(takeAllSpace ? constraintBox.getMaxWidth() : + Layouter.clamp(maxWidth, constraintBox.getMinWidth(), constraintBox.getMaxWidth()), + Layouter.clamp(sumHeight, constraintBox.getMinHeight(), constraintBox.getMaxHeight())); + } + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + LinkedList<FlatTextSpan> flatTextSpans = new LinkedList<>(); + rootSpan.flattenTextSpan(flatTextSpans::add); + + LinkedList<FlatTextSpan> line = new LinkedList<>(); + double usedUp = 0; + + double maxWidth = 0; + while (!flatTextSpans.isEmpty()) { + FlatTextSpan first = flatTextSpans.pollFirst(); + BrokenWordData brokenWordData = first.breakWord(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, breakWord); + usedUp += brokenWordData.getFirstWidth(); + line.add(brokenWordData.getFirst()); + + if (brokenWordData.getSecond() != null) { + flatTextSpans.addFirst(brokenWordData.getSecond()); + } + + if (brokenWordData.isBroken()) { + maxWidth = Math.max(maxWidth, usedUp); + usedUp = 0; + } + } + if (!line.isEmpty()) { + maxWidth = Math.max(maxWidth, usedUp); + } + + return maxWidth; + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + LinkedList<FlatTextSpan> flatTextSpans = new LinkedList<>(); + rootSpan.flattenTextSpan(flatTextSpans::add); + double remaining = width == 0 ? Double.POSITIVE_INFINITY : width; + double maxHeight = 0; + double sumHeight = 0; + while (!flatTextSpans.isEmpty()) { + FlatTextSpan first = flatTextSpans.pollFirst(); + + BrokenWordData brokenWordData = first.breakWord(remaining, width == 0 ? Double.POSITIVE_INFINITY : width, breakWord); + remaining -= brokenWordData.getFirstWidth(); + + if (brokenWordData.getFirst().value.length == 0 && first.value.length != 0 && remaining == width) { + return 0; + } + + maxHeight = Math.max(maxHeight, first.getHeight()); + + if (brokenWordData.getSecond() != null) { + flatTextSpans.addFirst(brokenWordData.getSecond()); + } + + if (brokenWordData.isBroken()) { + remaining = width == 0 ? Double.POSITIVE_INFINITY : width; + sumHeight += maxHeight; + maxHeight = 0; + } + } + sumHeight += maxHeight; + return sumHeight; + } + + @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) { + double x = 0; + double y = 0; + double width = buildContext.getSize().getWidth(); + double currentScale = buildContext.getAbsBounds().getWidth() / width; + for (RichLine richLine : richLines) { + if (align == TextAlign.RIGHT) + x = width - richLine.getWidth(); + else if (align == TextAlign.CENTER) + x = (width - richLine.getWidth()) / 2; + else + x = 0; + for (FlatTextSpan lineElement : richLine.getLineElements()) { + lineElement.textStyle.getFontRenderer() + .render(lineElement, x, y + richLine.getBaseline() - lineElement.getBaseline() + + lineElement.textStyle.getTopAscent() * lineElement.textStyle.getSize(), currentScale); + x += lineElement.getWidth(); + } + y += richLine.getHeight(); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/TextSpan.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/TextSpan.java new file mode 100644 index 00000000..abc78f51 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/TextSpan.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.launcher.guiv2.elements.richtext; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.CompiledTextStyle; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.ITextStyle; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.ParentDelegatingTextStyle; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class TextSpan { + private ITextStyle textStyle; + private String text; + private List<TextSpan> children = new ArrayList<>(); + public TextSpan(ITextStyle textStyle, String text) { + this.textStyle = textStyle; + this.text = text; + } + + public void addChild(TextSpan textSpan) { + if (textSpan.textStyle instanceof ParentDelegatingTextStyle) + ((ParentDelegatingTextStyle) textSpan.textStyle).setParent(textStyle); + children.add(textSpan); + } + + public void flattenTextSpan(Consumer<FlatTextSpan> appender) { + appender.accept(new FlatTextSpan(new CompiledTextStyle(textStyle), text.toCharArray())); + children.forEach(a -> a.flattenTextSpan(appender)); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/fonts/DefaultFontRenderer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/fonts/DefaultFontRenderer.java new file mode 100644 index 00000000..37c5e589 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/fonts/DefaultFontRenderer.java @@ -0,0 +1,310 @@ +/* + * 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.launcher.guiv2.elements.richtext.fonts; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.FlatTextSpan; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.ITextStyle; +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.texture.TextureUtil; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.io.IOUtils; +import org.lwjgl.opengl.GL11; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.List; + +public class DefaultFontRenderer implements FontRenderer { + public static DefaultFontRenderer DEFAULT_RENDERER = new DefaultFontRenderer(); + + private static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256]; + protected int[] charWidth = new int[256]; + public int FONT_HEIGHT = 9; + protected byte[] glyphData = new byte[65536]; + protected final ResourceLocation locationFontTexture = new ResourceLocation("textures/font/ascii.png"); + + public DefaultFontRenderer() { + readGlyphSizes(); + } + + public void onResourceManagerReload() { + this.readFontTexture(); + this.readGlyphSizes(); + } + + private void readFontTexture() { + BufferedImage bufferedimage; + try { + bufferedimage = TextureUtil.readBufferedImage( + Minecraft.getMinecraft().getResourceManager().getResource(this.locationFontTexture).getInputStream()); + } catch (IOException var17) { + throw new RuntimeException(var17); + } + + int i = bufferedimage.getWidth(); + int j = bufferedimage.getHeight(); + int[] aint = new int[i * j]; + bufferedimage.getRGB(0, 0, i, j, aint, 0, i); + int k = j / 16; + int l = i / 16; + int i1 = 1; + float f = 8.0F / (float)l; + + for(int j1 = 0; j1 < 256; ++j1) { + int k1 = j1 % 16; + int l1 = j1 / 16; + if (j1 == 32) { + this.charWidth[j1] = 3 + i1; + } + + int i2; + for(i2 = l - 1; i2 >= 0; --i2) { + int j2 = k1 * l + i2; + boolean flag = true; + + for(int k2 = 0; k2 < k && flag; ++k2) { + int l2 = (l1 * l + k2) * i; + if ((aint[j2 + l2] >> 24 & 255) != 0) { + flag = false; + } + } + + if (!flag) { + break; + } + } + + ++i2; + this.charWidth[j1] = (int)(0.5 + (double)((float)i2 * f)) + i1; + } + + } + + private void readGlyphSizes() { + InputStream inputstream = null; + + try { + inputstream = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream(); + inputstream.read(this.glyphData); + } catch (IOException var6) { + throw new RuntimeException(var6); + } finally { + IOUtils.closeQuietly(inputstream); + } + + } + + private ResourceLocation getUnicodePageLocation(int page) { + if (unicodePageLocations[page] == null) { + unicodePageLocations[page] = new ResourceLocation(String.format("textures/font/unicode_page_%02x.png", page)); + } + + return unicodePageLocations[page]; + } + + @Override + public double getWidth(char text, ITextStyle textStyle) { + double val; + if (text == ' ') { + val = 4; + } else { + int i = "ÀÁÂÈÊËÍÓÔÕÚßãõğİıŒœŞşŴŵžȇ\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αβΓπΣσμτΦΘΩδ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u0000".indexOf(text); + if (text > 0 && i != -1) { + val = this.charWidth[i]; + } else if (this.glyphData[text] != 0) { + int texStart = this.glyphData[text] >>> 4; + int texEnd = this.glyphData[text] & 15 + 1; + val = (texEnd - texStart) / 2.0 + 1; + } else { + val = 0; + } + } + return (val + (textStyle.isBold() ? 1 : 0)) * textStyle.getSize() / 8; + } + + @Override + public double getBaselineHeight(ITextStyle textStyle) { + return 7 * textStyle.getSize() / 8.0; + } + + public void render(WorldRenderer worldRenderer, FlatTextSpan lineElement, double x, double y, boolean isShadow) { + double startX = x; + if (isShadow) lineElement.textStyle.getShadowShader().useShader(); + + if (!isShadow) lineElement.textStyle.getTextShader().useShader(); + GlStateManager.enableTexture2D(); + worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + for (char c : lineElement.value) { + x += renderChar(worldRenderer, x, y+1, c, lineElement.textStyle); + } + draw(worldRenderer); + if (!isShadow) lineElement.textStyle.getTextShader().freeShader(); + + GlStateManager.disableTexture2D(); + double baseline = getBaselineHeight(lineElement.textStyle); + if (lineElement.textStyle.isStrikeThrough()) { + if (!isShadow) lineElement.textStyle.getStrikeThroughShader().useShader(); + worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); + worldRenderer.pos(startX, y + baseline/2, 0).endVertex(); + worldRenderer.pos(x, y + baseline/2, 0).endVertex(); + worldRenderer.pos(x, y +baseline/2+1, 0).endVertex(); + worldRenderer.pos(startX, y + baseline/2+1, 0).endVertex(); + draw(worldRenderer); + if (!isShadow) lineElement.textStyle.getStrikeThroughShader().freeShader(); + } + if (lineElement.textStyle.isUnderline()) { + if (!isShadow) lineElement.textStyle.getUnderlineShader().useShader(); + worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); + worldRenderer.pos(startX, y + baseline, 0).endVertex(); + worldRenderer.pos(x, y + baseline, 0).endVertex(); + worldRenderer.pos(x, y + baseline+1, 0).endVertex(); + worldRenderer.pos(startX, y + baseline+1, 0).endVertex(); + draw(worldRenderer); + if (!isShadow) lineElement.textStyle.getUnderlineShader().freeShader(); + } + + if (isShadow) lineElement.textStyle.getShadowShader().freeShader(); + GlStateManager.enableTexture2D(); // probably it expects it to be on + } + + @Override + public void render(FlatTextSpan lineElement, double x, double y, double currentScale) { + lastBound = null; + WorldRenderer worldRenderer = Tessellator.getInstance().getWorldRenderer(); + GlStateManager.disableTexture2D(); + GlStateManager.enableAlpha(); + if (lineElement.textStyle.getBackgroundShader() != null) { + lineElement.textStyle.getBackgroundShader().useShader(); + worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); + worldRenderer.pos(x, y, 0).endVertex(); + worldRenderer.pos(x + lineElement.getWidth(), y, 0).endVertex(); + worldRenderer.pos(x + lineElement.getWidth(), y + lineElement.textStyle.getSize(), 0).endVertex(); + worldRenderer.pos(x, y + lineElement.textStyle.getSize(), 0).endVertex(); + draw(worldRenderer); + lineElement.textStyle.getBackgroundShader().freeShader(); + } + y += lineElement.textStyle.getTopAscent(); + + if (lineElement.textStyle.isShadow()) + render(worldRenderer, lineElement, x+1,y+1, true); + render(worldRenderer, lineElement, x,y, false); + } + + + + private double renderChar(WorldRenderer worldRenderer, double x, double y, char ch, ITextStyle textStyle) { + if (ch == ' ') { + return 4.0F * textStyle.getSize() / 8.0; + } else { + int i = "ÀÁÂÈÊËÍÓÔÕÚßãõğİıŒœŞşŴŵžȇ\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αβΓπΣσμτΦΘΩδ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u0000".indexOf(ch); + return i != -1 ? this.renderDefaultChar(worldRenderer, x, y, i, textStyle) : this.renderUnicodeChar(worldRenderer, x, y, ch, textStyle); + } + } + + + private void draw(WorldRenderer renderer) { + renderer.finishDrawing(); + if (renderer.getVertexCount() > 0) { + VertexFormat vertexformat = renderer.getVertexFormat(); + int i = vertexformat.getNextOffset(); + ByteBuffer bytebuffer = renderer.getByteBuffer(); + List<VertexFormatElement> list = vertexformat.getElements(); + + int i1; + for(i1 = 0; i1 < list.size(); ++i1) { + VertexFormatElement vertexformatelement = (VertexFormatElement)list.get(i1); + vertexformatelement.getUsage().preDraw(vertexformat, i1, i, bytebuffer); + } + + GL11.glDrawArrays(renderer.getDrawMode(), 0, renderer.getVertexCount()); + i1 = 0; + + for(int j1 = list.size(); i1 < j1; ++i1) { + VertexFormatElement vertexFormatElement1 = (VertexFormatElement)list.get(i1); + vertexFormatElement1.getUsage().postDraw(vertexformat, i1, i, bytebuffer); + } + } + + renderer.reset(); + } + + private ResourceLocation lastBound; + public void bindTexture(WorldRenderer worldRenderer, ResourceLocation resourceLocation) { + if (resourceLocation.equals(lastBound)) return; + lastBound = resourceLocation; + draw(worldRenderer); + Minecraft.getMinecraft().getTextureManager().bindTexture(resourceLocation); + worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + } + + private double renderDefaultChar(WorldRenderer worldRenderer, double posX, double posY, int ch, ITextStyle textStyle) { + int texX = ch % 16 * 8; + int texY = ch / 16 * 8; + int italicsAddition = textStyle.isItalics() ? 1 : 0; + bindTexture(worldRenderer, this.locationFontTexture); + double texWidth = (this.charWidth[ch]); + double charWidth = (texWidth-1) * textStyle.getSize() / 8.0; + double charHeight = textStyle.getSize(); + // char width contains the gap between next char + + + worldRenderer.pos(posX + (float)italicsAddition, posY, 0.0F).tex((float)texX / 128.0F, (float)texY / 128.0F).endVertex(); + worldRenderer.pos(posX - (float)italicsAddition, posY + charHeight - 0.01F, 0.0F).tex((float)texX / 128.0F, ((float)texY + 7.99F) / 128.0F).endVertex(); + worldRenderer.pos(posX + charWidth+ (float)italicsAddition - 0.01F , posY+ charHeight - 0.01F, 0.0F).tex(((float)texX + texWidth - 1.01F) / 128.0F, ((float)texY + 7.99F) / 128.0F).endVertex(); + worldRenderer.pos(posX + charWidth - (float)italicsAddition - 0.01F, posY , 0.0F).tex(((float)texX + texWidth - 1.01F) / 128.0F, (float)texY / 128.0F).endVertex(); + + return texWidth * textStyle.getSize() / 8.0; + } + private double renderUnicodeChar(WorldRenderer worldRenderer, double posX, double posY, char ch, ITextStyle textStyle) { + if (this.glyphData[ch] == 0) { + return 0.0F; + } else { + int i = ch / 256; + bindTexture(worldRenderer, this.getUnicodePageLocation(i)); + float xStart = (float)(this.glyphData[ch] >>> 4); + float xEnd = (float)(this.glyphData[ch] & 15 + 1); + + float texX = (float)(ch % 16 * 16) + xStart; + float texY = (float)((ch & 255) / 16 * 16); + float texWidth = xEnd - xStart - 0.02F; + float italicSlope = textStyle.isItalics() ? 1.0F : 0.0F; + + double charWidth = texWidth * textStyle.getSize() / 16.0; + double charHeight = textStyle.getSize(); + + worldRenderer.pos(posX + italicSlope, posY, 0.0F) + .tex(texX / 256.0F, texY / 256.0F).endVertex(); + worldRenderer.pos(posX - italicSlope, posY + charHeight - 0.01F, 0.0F) + .tex(texX / 256.0F, (texY + 15.98F) / 256.0F).endVertex(); + worldRenderer.pos(posX + charWidth + italicSlope, posY + charHeight - 0.01F, 0.0F) + .tex((texX + texWidth) / 256.0F, (texY+15.98F) / 256.0F).endVertex(); + worldRenderer.pos(posX + charWidth - italicSlope, posY, 0.0F) + .tex((texX + texWidth) / 256.0F, (texY) / 256.0F).endVertex(); + return charWidth + textStyle.getSize() / 8.0; + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/fonts/FontRenderer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/fonts/FontRenderer.java new file mode 100644 index 00000000..a23702b4 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/fonts/FontRenderer.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.launcher.guiv2.elements.richtext.fonts; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.FlatTextSpan; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.styles.ITextStyle; + +public interface FontRenderer { + double getWidth(char text, ITextStyle textStyle); + // from y 0 going downward + double getBaselineHeight(ITextStyle textStyle); + + void render(FlatTextSpan lineElement, double x, double v, double currentScale); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/ChromaShader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/ChromaShader.java new file mode 100644 index 00000000..98b029be --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/ChromaShader.java @@ -0,0 +1,41 @@ +/* + * 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.launcher.guiv2.elements.richtext.shaders; + +import net.minecraft.client.renderer.GlStateManager; + +public class ChromaShader implements Shader { + private float r, g, b, a; + public ChromaShader(float chromaSpeed, int color) { + r = ((color >> 16) & 0xFF) / 255.0f; + g = ((color >> 8) & 0xFF) / 255.0f; + b = ((color) & 0xFF) / 255.0f; + a = ((color >> 24) & 0xFF) / 255.0f; + } + + + // argb= + @Override + public void useShader() { + GlStateManager.color(r,g,b,a); + } + + @Override + public void freeShader() {} +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/Shader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/Shader.java new file mode 100644 index 00000000..45163a16 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/Shader.java @@ -0,0 +1,24 @@ +/* + * 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.launcher.guiv2.elements.richtext.shaders; + +public interface Shader { + public void useShader(); + public void freeShader(); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/SingleColorShader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/SingleColorShader.java new file mode 100644 index 00000000..4ed2caf0 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/shaders/SingleColorShader.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.launcher.guiv2.elements.richtext.shaders; + +import net.minecraft.client.renderer.GlStateManager; + +public class SingleColorShader implements Shader{ + private float r, g, b, a; + + // argb + public SingleColorShader(int color) { + r = ((color >> 16) & 0xFF) / 255.0f; + g = ((color >> 8) & 0xFF) / 255.0f; + b = ((color) & 0xFF) / 255.0f; + a = ((color >> 24) & 0xFF) / 255.0f; + } + @Override + public void useShader() { + GlStateManager.color(r,g,b,a); + } + + @Override + public void freeShader() {} +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/CompiledTextStyle.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/CompiledTextStyle.java new file mode 100644 index 00000000..aa76fcde --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/CompiledTextStyle.java @@ -0,0 +1,146 @@ +/* + * 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.launcher.guiv2.elements.richtext.styles; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.fonts.FontRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader; + +public class CompiledTextStyle implements ITextStyle { + public Double size; + public Double topAscent; + public Double bottomAscent; + + public boolean bold; + public boolean italics; + public boolean strikeThrough; + public boolean underline; + public boolean outline; + public boolean shadow; + + public Shader backgroundShader; + public Shader textShader; + public Shader strikeThroughShader; + public Shader underlineShader; + public Shader outlineShader; + public Shader shadowShader; + + + public FontRenderer fontRenderer; + + public CompiledTextStyle(ITextStyle from) { + this.size = from.getSize(); + this.topAscent = from.getTopAscent(); + this.bottomAscent = from.getBottomAscent(); + + this.bold = from.isBold(); + this.italics = from.isItalics(); + this.strikeThrough = from.isStrikeThrough(); + this.underline = from.isUnderline(); + this.outline = from.isOutline(); + this.shadow = from.isShadow(); + + this.backgroundShader = from.getBackgroundShader(); + this.textShader = from.getTextShader(); + this.strikeThroughShader = from.getStrikeThroughShader(); + this.underlineShader = from.getUnderlineShader(); + this.outlineShader = from.getOutlineShader(); + this.shadowShader = from.getShadowShader(); + this.fontRenderer = from.getFontRenderer(); + } + + @Override + public Double getSize() { + return size; + } + + @Override + public Double getTopAscent() { + return topAscent; + } + + @Override + public Double getBottomAscent() { + return bottomAscent; + } + + @Override + public Boolean isBold() { + return bold; + } + + @Override + public Boolean isItalics() { + return italics; + } + + @Override + public Boolean isStrikeThrough() { + return strikeThrough; + } + + @Override + public Boolean isUnderline() { + return underline; + } + + @Override + public Boolean isOutline() { + return outline; + } + + @Override + public Boolean isShadow() { + return shadow; + } + + @Override + public Shader getBackgroundShader() { + return backgroundShader; + } + + @Override + public Shader getTextShader() { + return textShader; + } + + @Override + public Shader getStrikeThroughShader() { + return strikeThroughShader; + } + + @Override + public Shader getUnderlineShader() { + return underlineShader; + } + + @Override + public Shader getOutlineShader() { + return outlineShader; + } + + @Override + public Shader getShadowShader() { + return shadowShader; + } + + @Override + public FontRenderer getFontRenderer() { + return fontRenderer; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java new file mode 100644 index 00000000..48cee96f --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java @@ -0,0 +1,52 @@ +/* + * 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.launcher.guiv2.elements.richtext.styles; + +public interface ITextStyle { + Double getSize(); + + Double getTopAscent(); + Double getBottomAscent(); + + Boolean isBold(); + + Boolean isItalics(); + + Boolean isStrikeThrough(); + + Boolean isUnderline(); + + Boolean isOutline(); + + Boolean isShadow(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getBackgroundShader(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getTextShader(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getStrikeThroughShader(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getUnderlineShader(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getOutlineShader(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getShadowShader(); + + kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.fonts.FontRenderer getFontRenderer(); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ParentDelegatingTextStyle.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ParentDelegatingTextStyle.java new file mode 100644 index 00000000..621ac2cd --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ParentDelegatingTextStyle.java @@ -0,0 +1,158 @@ +/* + * 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.launcher.guiv2.elements.richtext.styles; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.fonts.DefaultFontRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.fonts.FontRenderer; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.SingleColorShader; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +@Setter @Accessors(chain = true) +public class ParentDelegatingTextStyle implements ITextStyle { + public Double size; + public Double topAscent; + public Double bottomAscent; + + + + public Boolean bold; + public Boolean italics; + public Boolean strikeThrough; + public Boolean underline; + public Boolean outline; + public Boolean shadow; + + public Shader backgroundShader; + public Shader textShader; + public Shader strikeThroughShader; + public Shader underlineShader; + public Shader outlineShader; + public Shader shadowShader; + + + @Getter @Setter + public ITextStyle parent; + public FontRenderer fontRenderer; + + public static ParentDelegatingTextStyle ofDefault() { + ParentDelegatingTextStyle parentDelegatingTextStyle = new ParentDelegatingTextStyle(); + parentDelegatingTextStyle.size = 8.0; + parentDelegatingTextStyle.topAscent = 0.0; + parentDelegatingTextStyle.bottomAscent = 1 / 8.0; + parentDelegatingTextStyle.bold = false; + parentDelegatingTextStyle.italics = false; + parentDelegatingTextStyle.strikeThrough = false; + parentDelegatingTextStyle.underline = false; + parentDelegatingTextStyle.shadow = false; + parentDelegatingTextStyle.outline = false; + + parentDelegatingTextStyle.backgroundShader = new SingleColorShader(0xFFFFFFFF); + parentDelegatingTextStyle.textShader = new SingleColorShader(0xFFFFFFFF); + parentDelegatingTextStyle.strikeThroughShader = new SingleColorShader(0xFF000000); + parentDelegatingTextStyle.underlineShader = new SingleColorShader(0xFF000000); + parentDelegatingTextStyle.outlineShader = new SingleColorShader(0xFF000000); + parentDelegatingTextStyle.shadowShader = new SingleColorShader(0xFF000000); + + parentDelegatingTextStyle.fontRenderer = DefaultFontRenderer.DEFAULT_RENDERER; + return parentDelegatingTextStyle; + } + + @Override + public Double getSize() { + return parent != null && size == null ? parent.getSize() : size; + } + + @Override + public Double getTopAscent() { + return parent != null && topAscent == null ? parent.getTopAscent() : topAscent; + } + + @Override + public Double getBottomAscent() { + return parent != null && bottomAscent == null ? parent.getBottomAscent() : bottomAscent; + } + + @Override + public Boolean isBold() { + return parent != null && bold == null ? parent.isBold() : bold; + } + + @Override + public Boolean isItalics() { + return parent != null && italics == null ? parent.isItalics() : italics; + } + + @Override + public Boolean isOutline() { + return parent != null && outline == null ? parent.isOutline() : outline; + } + + @Override + public Boolean isShadow() { + return parent != null && shadow == null ? parent.isShadow() : shadow; + } + + @Override + public Boolean isStrikeThrough() { + return parent != null && strikeThrough == null ? parent.isStrikeThrough() : strikeThrough; + } + + @Override + public Boolean isUnderline() { + return parent != null && underline == null ? parent.isUnderline() : underline; + } + + @Override + public FontRenderer getFontRenderer() { + return parent != null && fontRenderer == null ? parent.getFontRenderer() : fontRenderer; + } + + @Override + public Shader getShadowShader() { + return parent != null && shadowShader == null ? parent.getShadowShader() : shadowShader; + } + + @Override + public Shader getBackgroundShader() { + return parent != null && backgroundShader == null ? parent.getBackgroundShader() : backgroundShader; + } + + @Override + public Shader getOutlineShader() { + return parent != null && outlineShader == null ? parent.getOutlineShader() : outlineShader; + } + + @Override + public Shader getStrikeThroughShader() { + return parent != null && strikeThroughShader == null ? parent.getStrikeThroughShader() : strikeThroughShader; + } + + @Override + public Shader getTextShader() { + return parent != null && textShader == null ? parent.getTextShader() : textShader; + } + + @Override + public Shader getUnderlineShader() { + return parent != null && underlineShader == null ? parent.getUnderlineShader() : underlineShader; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/Layouter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/Layouter.java new file mode 100644 index 00000000..c7cc4c11 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/Layouter.java @@ -0,0 +1,43 @@ +/* + * 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.launcher.guiv2.layouter; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.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; + } + + default boolean canCutRequest() { + return false; + } + + default double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return 0; + } + default double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return 0; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/NullLayouter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/NullLayouter.java new file mode 100644 index 00000000..722784eb --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.layouter; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/SingleChildPassingLayouter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/SingleChildPassingLayouter.java new file mode 100644 index 00000000..b770a4b9 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/layouter/SingleChildPassingLayouter.java @@ -0,0 +1,53 @@ +/* + * 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.launcher.guiv2.layouter; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.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()); + } + + + @Override + public double getMaxIntrinsicWidth(DomElement buildContext, double height) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicWidth(buildContext.getChildren().get(0), height); + } + + @Override + public double getMaxIntrinsicHeight(DomElement buildContext, double width) { + return buildContext.getChildren().isEmpty() ? 0 : buildContext.getChildren().get(0).getLayouter().getMaxIntrinsicHeight(buildContext.getChildren().get(0), width); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Animation.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Animation.java new file mode 100644 index 00000000..f42439aa --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/ConstraintBox.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/ConstraintBox.java new file mode 100644 index 00000000..86289120 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/IPosition.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/IPosition.java new file mode 100644 index 00000000..e3a14f91 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.primitive; + +public interface IPosition { + double getX(); + + double getY(); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/IRect.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/IRect.java new file mode 100644 index 00000000..f9655803 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/ISize.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/ISize.java new file mode 100644 index 00000000..a6199400 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Position.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Position.java new file mode 100644 index 00000000..5d0b7ac4 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Rect.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Rect.java new file mode 100644 index 00000000..310382f2 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Size.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/primitive/Size.java new file mode 100644 index 00000000..32803616 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/DrawNothingRenderer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/DrawNothingRenderer.java new file mode 100644 index 00000000..2ba4f260 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.renderer; + +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/OnlyChildrenRenderer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/OnlyChildrenRenderer.java new file mode 100644 index 00000000..267c6b16 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/OnlyChildrenRenderer.java @@ -0,0 +1,52 @@ +/* + * 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.launcher.guiv2.renderer; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.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(); + if (original == null) continue; + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/Renderer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/Renderer.java new file mode 100644 index 00000000..164447cc --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/Renderer.java @@ -0,0 +1,36 @@ +/* + * 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.launcher.guiv2.renderer; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.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) { + if (element.getRelativeBound() == null) return pos; + return new Position(pos.getX() - element.getRelativeBound().getX(), pos.getY() - element.getRelativeBound().getY());} +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/RenderingContext.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/RenderingContext.java new file mode 100644 index 00000000..f227b446 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/RenderingContext.java @@ -0,0 +1,142 @@ +/* + * 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.launcher.guiv2.renderer; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; +import kr.syeyoung.dungeonsguide.launcher.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 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 Rectangle currentClip() { + return clips.empty() ? null : clips.peek(); + } + + public void pushClip(Rect absBounds, Size size, double x, double y, double width, double height) { + if (width < 0 || height < 0) { + width = 0; + height = 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/SingleChildRenderer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/SingleChildRenderer.java new file mode 100644 index 00000000..cf2645f3 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/renderer/SingleChildRenderer.java @@ -0,0 +1,52 @@ +/* + * 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.launcher.guiv2.renderer; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.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(); + if (original == null) return; + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/view/TestPopup.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/view/TestPopup.java new file mode 100644 index 00000000..3f8f90e2 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/view/TestPopup.java @@ -0,0 +1,35 @@ +/* + * 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.launcher.guiv2.view; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.PopupMgr; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedImportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.On; +import net.minecraft.util.ResourceLocation; + +public class TestPopup extends AnnotatedImportOnlyWidget { + public TestPopup() { + super(new ResourceLocation("dungeons_guide_loader:gui/testpopup.gui")); + } + + @On(functionName = "close") + public void onClick() { + PopupMgr.getPopupMgr(getDomElement()).closePopup(null); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/view/TestView.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/view/TestView.java new file mode 100644 index 00000000..b1bb09ff --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/view/TestView.java @@ -0,0 +1,50 @@ +/* + * 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.launcher.guiv2.view; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.PopupMgr; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedImportOnlyWidget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.On; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; + +public class TestView extends AnnotatedImportOnlyWidget { + public TestView() { + super(new ResourceLocation("dungeons_guide_loader: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(), null); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedExportOnlyWidget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedExportOnlyWidget.java new file mode 100644 index 00000000..3dfdf08a --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedExportOnlyWidget.java @@ -0,0 +1,88 @@ +/* + * 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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.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 : getExportedAttributes().values()) { + value.unexportAll(); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedImportOnlyWidget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedImportOnlyWidget.java new file mode 100644 index 00000000..29bcc4ef --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedImportOnlyWidget.java @@ -0,0 +1,125 @@ +/* + * 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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.On; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.Parser; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.ParserElement; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.reflect.FieldUtils; + +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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedWidget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedWidget.java new file mode 100644 index 00000000..6b33b739 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/AnnotatedWidget.java @@ -0,0 +1,151 @@ +/* + * 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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Passthrough; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Passthroughs; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.Parser; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DelegatingWidget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DelegatingWidget.java new file mode 100644 index 00000000..d8069963 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DelegatingWidget.java @@ -0,0 +1,99 @@ +/* + * 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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.Parser; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.ParserElement; +import net.minecraft.util.ResourceLocation; + +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DelegatingWidgetConverter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DelegatingWidgetConverter.java new file mode 100644 index 00000000..0f6e5b88 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DelegatingWidgetConverter.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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import net.minecraft.util.ResourceLocation; + +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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java new file mode 100644 index 00000000..8ca03ab5 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java @@ -0,0 +1,107 @@ +/* + * 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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.image.ResourceImage; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.image.URLImage; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.PopupMgr; +import kr.syeyoung.dungeonsguide.launcher.guiv2.view.TestView; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.Parser; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.ParserException; +import kr.syeyoung.dungeonsguide.launcher.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) { + if (!converters.containsKey(name)) + System.out.println("Try to get nonexistent widget "+name); + return converters.get(name); + } + + static { + register("stack", new ExportedWidgetConverter(Stack::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("ConstrainedBox", new ExportedWidgetConverter(ConstrainedBox::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("AbstractToggleButton", new ExportedWidgetConverter(ToggleButton::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("IntrinsicWidth", new ExportedWidgetConverter(IntrinsicWidth::new)); + register("IntrinsicHeight", new ExportedWidgetConverter(IntrinsicHeight::new)); + register("TestView", new ExportedWidgetConverter(TestView::new)); + register("RoundRect", new ExportedWidgetConverter(RoundRect::new)); + register("CircularRect", new ExportedWidgetConverter(CircularRect::new)); + register("Navigator", new ExportedWidgetConverter(Navigator::new)); + register("Stencil", new ExportedWidgetConverter(Stencil::new)); + register("InvertStencil", new ExportedWidgetConverter(NegativeStencil::new)); + register("WrapGrid", new ExportedWidgetConverter(Wrap::new)); + + register("ColorButton", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/simpleButton.gui"))); + register("RoundButton", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/dgButton.gui"))); + register("IconButton", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/iconButton.gui"))); + register("SimpleToggleButton", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/simpleToggleButton.gui"))); + register("SimpleHorizontalScrollBar", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/simpleHorizontalScrollBar.gui"))); + register("SimpleVerticalScrollBar", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/simpleVerticalScrollBar.gui"))); + register("SlowList", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/slowlist.gui"))); + register("size", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/size.gui"))); + register("ResourceImage", new DelegatingWidgetConverter(new ResourceLocation("dungeons_guide_loader:gui/elements/ratioResourceImage.gui"))); + register("UrlImage", new ExportedWidgetConverter(URLImage::new)); + register("SelectiveContainer", new ExportedWidgetConverter(SelectiveContainer::new)); + } + + 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 occurred while parsing "+resourceLocation, e); + } + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ExportedWidget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ExportedWidget.java new file mode 100644 index 00000000..e8fe6798 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; + +public interface ExportedWidget { + <T> BindableAttribute<T> getExportedAttribute(String attributeName); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ExportedWidgetConverter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ExportedWidgetConverter.java new file mode 100644 index 00000000..5410a576 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ExportedWidgetConverter.java @@ -0,0 +1,42 @@ +/* + * 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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; + +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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ImportingWidget.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ImportingWidget.java new file mode 100644 index 00000000..2a5921e3 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ParsedWidgetConverter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/ParsedWidgetConverter.java new file mode 100644 index 00000000..534280e3 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/PropByPropParsedWidgetConverter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/PropByPropParsedWidgetConverter.java new file mode 100644 index 00000000..8f6e2feb --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.xml; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.BindableAttribute; +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.ParserElement; +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.ParserElementList; +import kr.syeyoung.dungeonsguide.launcher.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.*; +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(); + + Set<String> boundSlots = new HashSet<>(); + for (String attribute : element.getAttributes()) { + if (attribute.startsWith("bind:")) { + String name = attribute.substring(5); + String variable = element.getAttributeValue(attribute); + if (name.startsWith("_")) + boundSlots.add(name); + + 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()) { + if (boundSlots.contains("_"+stringListEntry.getKey())) continue; + 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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/StringConversions.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/StringConversions.java new file mode 100644 index 00000000..3d3a3639 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/StringConversions.java @@ -0,0 +1,49 @@ +/* + * 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.launcher.guiv2.xml; + +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()+": "+val); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Bind.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Bind.java new file mode 100644 index 00000000..b4fff104 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Export.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Export.java new file mode 100644 index 00000000..76858878 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/On.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/On.java new file mode 100644 index 00000000..89e125d9 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Passthrough.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Passthrough.java new file mode 100644 index 00000000..31cc557e --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Passthroughs.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Passthroughs.java new file mode 100644 index 00000000..d7888981 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/annotations/Passthroughs.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.launcher.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.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Passthroughs { + Passthrough[] value(); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/Parser.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/Parser.java new file mode 100644 index 00000000..ad91be35 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.xml.data; + +import java.io.Closeable; + +public interface Parser extends AutoCloseable, Closeable { + ParserElement getRootNode(); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserElement.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserElement.java new file mode 100644 index 00000000..2e2259e7 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserElementList.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserElementList.java new file mode 100644 index 00000000..af5e224c --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserElementList.java @@ -0,0 +1,24 @@ +/* + * 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.launcher.guiv2.xml.data; + +import java.util.List; + +public interface ParserElementList extends List<ParserElement> { +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/ParserException.java new file mode 100644 index 00000000..9364f907 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/W3CBackedParser.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/W3CBackedParser.java new file mode 100644 index 00000000..31d77b36 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/W3CBackedParserElement.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/W3CBackedParserElement.java new file mode 100644 index 00000000..4f4e19dd --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/W3CBackedParserElement.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.launcher.guiv2.xml.data; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.StringConversions; +import org.w3c.dom.*; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +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/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/WidgetList.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/data/WidgetList.java new file mode 100644 index 00000000..418e9b48 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/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.launcher.guiv2.xml.data; + +import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; + +import java.util.List; + +public interface WidgetList extends List<Widget> { +} |