From dd07024d53bcaaf447ab4e1221985cc75b2acb05 Mon Sep 17 00:00:00 2001 From: Basique Date: Sun, 30 Jul 2023 16:14:08 +0300 Subject: [PATCH 01/20] first steps towards multiple windows --- .../owo/ui/window/FramebufferWindow.java | 165 ++++++++++++++++++ .../wispforest/owo/ui/window/OwoWindow.java | 120 +++++++++++++ .../owo/ui/window/WindowClosed.java | 15 ++ .../owo/ui/window/WindowMouseMoved.java | 15 ++ .../owo/ui/window/WindowResized.java | 15 ++ .../io/wispforest/owo/util/OwoGlfwUtil.java | 37 ++++ .../io/wispforest/uwu/client/UwuClient.java | 2 + .../wispforest/uwu/client/UwuTestWindow.java | 42 +++++ 8 files changed, 411 insertions(+) create mode 100644 src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/OwoWindow.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowClosed.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowResized.java create mode 100644 src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java create mode 100644 src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java new file mode 100644 index 00000000..1cb9b0bb --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -0,0 +1,165 @@ +package io.wispforest.owo.ui.window; + +import com.mojang.blaze3d.systems.RenderSystem; +import io.wispforest.owo.util.EventSource; +import io.wispforest.owo.util.EventStream; +import io.wispforest.owo.util.OwoGlfwUtil; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.GlDebug; +import net.minecraft.client.gl.SimpleFramebuffer; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.NativeResource; + +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.glfw.GLFW.*; + +public class FramebufferWindow implements AutoCloseable { + private final int width; + private final int height; + private final long handle; + private final Framebuffer framebuffer; + private int localFramebuffer = 0; + protected final MinecraftClient client = MinecraftClient.getInstance(); + + private final List disposeList; + + private final EventStream windowClosedEvents = WindowClosed.newStream(); + private final EventStream windowResizedEvents = WindowResized.newStream(); + private final EventStream mouseMovedEvents = WindowMouseMoved.newStream(); + + public FramebufferWindow(int width, int height, String name, long parentContext) { + this.width = width; + this.height = height; + + try (var ignored = OwoGlfwUtil.setContext(0)) { + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1); + handle = glfwCreateWindow(width, height, name, 0, parentContext); + + if (handle == 0) { + throw new IllegalStateException("OwoWindow creation failed due to GLFW error"); + } + + glfwMakeContextCurrent(handle); + } + + this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + + try (var ignored = OwoGlfwUtil.setContext(this.handle)) { + GlDebug.enableDebug(client.options.glDebugVerbosity, true); + } + + initLocalFramebuffer(); + + this.disposeList = new ArrayList<>(); + glfwSetWindowCloseCallback(handle, stowAndReturn(GLFWWindowCloseCallback.create(window -> { + windowClosedEvents.sink().onWindowClosed(); + }))); + glfwSetFramebufferSizeCallback(handle, stowAndReturn(GLFWFramebufferSizeCallback.create(this::sizeChanged))); + glfwSetCursorPosCallback(handle, stowAndReturn(GLFWCursorPosCallback.create((window, xpos, ypos) -> { + mouseMovedEvents.sink().onMouseMoved(xpos, ypos); + }))); + glfwSetMouseButtonCallback(handle, stowAndReturn(GLFWMouseButtonCallback.create((window, button, action, mods) -> { +// onMouseButton.invoker().onMouseButton(button, action, mods); + }))); +// disposeList.add(glfwSetScrollCallback(handle, (window, xoffset, yoffset) -> { +// onMouseScroll.invoker().onMouseScroll(xoffset, yoffset); +// })); +// disposeList.add(glfwSetDropCallback(handle, (window, count, names) -> { +// Path[] paths = new Path[count]; +// +// for (int j = 0; j < count; ++j) { +// paths[j] = Paths.get(GLFWDropCallback.getName(names, j)); +// } +// +// onFilesDropped.invoker().onFilesDropped(paths); +// })); + } + + private T stowAndReturn(T resource) { + this.disposeList.add(resource); + return resource; + } + + public int width() { + return width; + } + + public int height() { + return height; + } + + public Framebuffer framebuffer() { + return framebuffer; + } + + public EventSource windowClosed() { + return windowClosedEvents.source(); + } + + public EventSource windowResized() { + return windowResizedEvents.source(); + } + + public EventSource mouseMoved() { + return mouseMovedEvents.source(); + } + + public void present() { + try (var ignored = OwoGlfwUtil.setContext(handle)) { + // This code intentionally doesn't use Minecraft's RenderSystem + // class, as it caches GL state that is invalid on this context. + GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, localFramebuffer); + GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, 0); + + GL32.glClearColor(1, 1, 1, 1); + GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); + GL32.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL32.GL_COLOR_BUFFER_BIT, GL32.GL_NEAREST); + + RenderSystem.flipFrame(handle); + } + } + + private void initLocalFramebuffer() { + if (localFramebuffer != 0) { + GL32.glDeleteFramebuffers(localFramebuffer); + } + + this.localFramebuffer = GL32.glGenFramebuffers(); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); + + int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); + if (status != GL32.GL_FRAMEBUFFER_COMPLETE) + throw new IllegalStateException("Failed to create local framebuffer!"); + } + + protected void sizeChanged(long handle, int width, int height) { + if (framebuffer.viewportWidth == width && framebuffer.viewportHeight == height) return; + + framebuffer.resize(width, height, MinecraftClient.IS_SYSTEM_MAC); + initLocalFramebuffer(); + + windowResizedEvents.sink().onWindowResized(width, height); + } + + public boolean closed() { + return this.disposeList.isEmpty(); + } + + @Override + public void close() { + this.disposeList.forEach(NativeResource::free); + this.disposeList.clear(); + glfwDestroyWindow(this.handle); + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java new file mode 100644 index 00000000..8839f066 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -0,0 +1,120 @@ +package io.wispforest.owo.ui.window; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.systems.VertexSorter; +import io.wispforest.owo.Owo; +import io.wispforest.owo.ui.core.OwoUIAdapter; +import io.wispforest.owo.ui.core.ParentComponent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.render.DiffuseLighting; +import net.minecraft.client.util.math.MatrixStack; +import org.joml.Matrix4f; +import org.lwjgl.opengl.GL32; + +public abstract class OwoWindow extends FramebufferWindow { + private int scaleFactor; + private int scaledWidth; + private int scaledHeight; + private final OwoUIAdapter adapter; + + private int mouseX = -1; + private int mouseY = -1; + + public OwoWindow(int width, int height, String name, long parentContext) { + super(width, height, name, parentContext); + recalculateScale(); + + this.adapter = createAdapter(); + build(this.adapter.rootComponent); + this.adapter.inflateAndMount(); + + windowClosed().subscribe(this::close); + windowResized().subscribe((newWidth, newHeight) -> { + recalculateScale(); + }); + mouseMoved().subscribe((x, y) -> { + this.mouseX = (int) (x / scaleFactor); + this.mouseY = (int) (y / scaleFactor); + }); + } + + protected abstract OwoUIAdapter createAdapter(); + + protected abstract void build(R rootComponent); + + public void recalculateScale() { + int guiScale = MinecraftClient.getInstance().options.getGuiScale().getValue(); + boolean forceUnicodeFont = MinecraftClient.getInstance().options.getForceUnicodeFont().getValue(); + + int factor = 1; + + while ( + factor != guiScale + && factor < this.width() + && factor < this.height() + && this.width() / (factor + 1) >= 320 + && this.height() / (factor + 1) >= 240 + ) { + ++factor; + } + + if (forceUnicodeFont && factor % 2 != 0) { + ++factor; + } + + this.scaleFactor = factor; + this.scaledWidth = (int) Math.ceil((double) this.width() / scaleFactor); + this.scaledHeight = (int) Math.ceil((double) this.height() / scaleFactor); + } + + public void render() { + framebuffer().beginWrite(true); + + RenderSystem.clearColor(0, 0, 0, 1); + RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + + Matrix4f matrix4f = new Matrix4f() + .setOrtho( + 0.0F, + scaledWidth(), + scaledHeight(), + 0.0F, + 1000.0F, + 21000.0F + ); + RenderSystem.setProjectionMatrix(matrix4f, VertexSorter.BY_Z); + MatrixStack matrixStack = RenderSystem.getModelViewStack(); + matrixStack.push(); + matrixStack.loadIdentity(); + matrixStack.translate(0.0F, 0.0F, -11000.0F); + RenderSystem.applyModelViewMatrix(); + DiffuseLighting.enableGuiDepthLighting(); + + var consumers = client.getBufferBuilders().getEntityVertexConsumers(); + adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getTickDelta()); + consumers.draw(); + + framebuffer().endWrite(); + + present(); + } + + public int scaleFactor() { + return scaleFactor; + } + + public int scaledWidth() { + return scaledWidth; + } + + public int scaledHeight() { + return scaledHeight; + } + + @Override + public void close() { + adapter.dispose(); + super.close(); + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowClosed.java b/src/main/java/io/wispforest/owo/ui/window/WindowClosed.java new file mode 100644 index 00000000..3266d704 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowClosed.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.ui.window; + +import io.wispforest.owo.util.EventStream; + +public interface WindowClosed { + void onWindowClosed(); + + static EventStream newStream() { + return new EventStream<>(subscribers -> () -> { + for (var subscriber : subscribers) { + subscriber.onWindowClosed(); + } + }); + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java b/src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java new file mode 100644 index 00000000..c29649d1 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.ui.window; + +import io.wispforest.owo.util.EventStream; + +public interface WindowMouseMoved { + void onMouseMoved(double x, double y); + + static EventStream newStream() { + return new EventStream<>(subscribers -> (x, y) -> { + for (var subscriber : subscribers) { + subscriber.onMouseMoved(x, y); + } + }); + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowResized.java b/src/main/java/io/wispforest/owo/ui/window/WindowResized.java new file mode 100644 index 00000000..c0505ca2 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowResized.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.ui.window; + +import io.wispforest.owo.util.EventStream; + +public interface WindowResized { + void onWindowResized(int newWidth, int newHeight); + + static EventStream newStream() { + return new EventStream<>(subscribers -> (newWidth, newHeight) -> { + for (var subscriber : subscribers) { + subscriber.onWindowResized(newWidth, newHeight); + } + }); + } +} diff --git a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java new file mode 100644 index 00000000..ae57fe5d --- /dev/null +++ b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java @@ -0,0 +1,37 @@ +package io.wispforest.owo.util; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +public final class OwoGlfwUtil { + private OwoGlfwUtil() { + + } + + public static ContextRestorer setContext(long handle) { + long old = GLFW.glfwGetCurrentContext(); + GLFW.glfwMakeContextCurrent(handle); + return new ContextRestorer(old); + } + + public static class GlfwException extends RuntimeException { + public GlfwException(String message) { + super(message); + } + } + + public static class ContextRestorer implements AutoCloseable { + private final long oldContext; + + private ContextRestorer(long old) { + this.oldContext = old; + } + + @Override + public void close() { + GLFW.glfwMakeContextCurrent(oldContext); + } + } +} diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuClient.java b/src/testmod/java/io/wispforest/uwu/client/UwuClient.java index 646c91b2..f993ac5f 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuClient.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuClient.java @@ -149,6 +149,8 @@ public void onInitializeClient() { instance.alignComponentToHandledScreenCoordinates(button, 125, 65); }, InventoryScreen.class); + + UwuTestWindow.init(); } public record WeirdMessage(int e) {} diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java new file mode 100644 index 00000000..8f0155fe --- /dev/null +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -0,0 +1,42 @@ +package io.wispforest.uwu.client; + +import io.wispforest.owo.ui.component.Components; +import io.wispforest.owo.ui.container.Containers; +import io.wispforest.owo.ui.container.FlowLayout; +import io.wispforest.owo.ui.core.Insets; +import io.wispforest.owo.ui.core.OwoUIAdapter; +import io.wispforest.owo.ui.window.OwoWindow; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +public class UwuTestWindow extends OwoWindow { + public UwuTestWindow() { + super(640, 480, "uωu test window!", MinecraftClient.getInstance().getWindow().getHandle()); + } + + public static void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(client -> { + var window = new UwuTestWindow(); + + ClientTickEvents.END_CLIENT_TICK.register(client1 -> { + if (window.closed()) return; + + window.render(); + }); + }); + } + + @Override + protected OwoUIAdapter createAdapter() { + return OwoUIAdapter.createWithoutScreen(0, 0, scaledWidth(), scaledHeight(), Containers::verticalFlow); + } + + @Override + protected void build(FlowLayout rootComponent) { + rootComponent.margins(Insets.of(10)); + rootComponent.child(Components.label(Text.literal("Honestly quite shrimple"))); + rootComponent.child(Components.button(Text.literal("Honestly quite shrimple"), unused -> {})); + } +} From 38dc6e3b5d68f52f351714de639748e0cd5d11d7 Mon Sep 17 00:00:00 2001 From: Basique Date: Mon, 31 Jul 2023 06:20:42 +0300 Subject: [PATCH 02/20] i can't believe it's progress --- .../owo/mixin/MinecraftClientMixin.java | 5 ++ .../wispforest/owo/ui/core/OwoUIAdapter.java | 20 ++++- .../wispforest/owo/ui/util/ScissorStack.java | 19 +++-- .../owo/ui/window/CurrentWindowContext.java | 70 ++++++++++++++++++ .../owo/ui/window/FramebufferWindow.java | 64 ++++++++++++---- .../wispforest/owo/ui/window/OpenWindows.java | 42 +++++++++++ .../wispforest/owo/ui/window/OwoWindow.java | 74 ++++++++++++------- .../owo/ui/window/WindowMouseButton.java | 15 ++++ .../owo/ui/window/WindowMouseScrolled.java | 15 ++++ .../io/wispforest/owo/util/OwoGlfwUtil.java | 5 ++ .../java/io/wispforest/owo/util/Wisdom.java | 6 +- .../uwu/client/ComponentTestScreen.java | 1 + .../io/wispforest/uwu/client/UwuClient.java | 3 +- .../wispforest/uwu/client/UwuTestWindow.java | 31 ++++---- 14 files changed, 294 insertions(+), 76 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/OpenWindows.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java diff --git a/src/main/java/io/wispforest/owo/mixin/MinecraftClientMixin.java b/src/main/java/io/wispforest/owo/mixin/MinecraftClientMixin.java index 63555621..ca30cd18 100644 --- a/src/main/java/io/wispforest/owo/mixin/MinecraftClientMixin.java +++ b/src/main/java/io/wispforest/owo/mixin/MinecraftClientMixin.java @@ -1,5 +1,6 @@ package io.wispforest.owo.mixin; +import io.wispforest.owo.ui.window.OpenWindows; import io.wispforest.owo.util.OwoFreezer; import net.minecraft.client.MinecraftClient; import net.minecraft.client.RunArgs; @@ -28,5 +29,9 @@ private void afterQuiltHook(RunArgs args, CallbackInfo ci) { OwoFreezer.freeze(); } + @Inject(method = "render", at = @At(value = "INVOKE_STRING", args = "ldc=yield", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V")) + private void renderAllWindows(CallbackInfo ci) { + OpenWindows.renderAll(); + } } diff --git a/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java b/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java index cdb15418..53c37f26 100644 --- a/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java +++ b/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java @@ -5,6 +5,8 @@ import io.wispforest.owo.Owo; import io.wispforest.owo.renderdoc.RenderDoc; import io.wispforest.owo.ui.util.CursorAdapter; +import io.wispforest.owo.ui.window.CurrentWindowContext; +import io.wispforest.owo.ui.window.OwoWindow; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.Drawable; @@ -56,6 +58,16 @@ protected OwoUIAdapter(int x, int y, int width, int height, R rootComponent) { this.rootComponent = rootComponent; } + protected OwoUIAdapter(int x, int y, int width, int height, R rootComponent, OwoWindow window) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.cursorAdapter = CursorAdapter.ofWindow(window.handle()); + this.rootComponent = rootComponent; + } + /** * Create a UI adapter for the given screen. This also sets it up * to be rendered and receive input events, without needing you to @@ -93,6 +105,11 @@ public static OwoUIAdapter createWithoutScreen(in return new OwoUIAdapter<>(x, y, width, height, rootComponent); } + public static OwoUIAdapter create(OwoWindow window, BiFunction rootComponentMaker) { + var rootComponent = rootComponentMaker.apply(Sizing.fill(100), Sizing.fill(100)); + return new OwoUIAdapter<>(0, 0, window.scaledWidth(), window.scaledHeight(), rootComponent, window); + } + /** * Begin the layout process of the UI tree and * mount the tree once the layout is inflated @@ -167,14 +184,13 @@ public void render(DrawContext context, int mouseX, int mouseY, float partialTic if (this.captureFrame) RenderDoc.startFrameCapture(); final var delta = MinecraftClient.getInstance().getLastFrameDuration(); - final var window = MinecraftClient.getInstance().getWindow(); this.rootComponent.update(delta, mouseX, mouseY); RenderSystem.enableDepthTest(); GlStateManager._enableScissorTest(); - GlStateManager._scissorBox(0, 0, window.getFramebufferWidth(), window.getFramebufferHeight()); + GlStateManager._scissorBox(0, 0, CurrentWindowContext.framebufferWidth(), CurrentWindowContext.framebufferHeight()); this.rootComponent.draw(owoContext, mouseX, mouseY, partialTicks, delta); GlStateManager._disableScissorTest(); diff --git a/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java b/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java index 5259fa6b..f52a89af 100644 --- a/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java +++ b/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.ui.core.Component; import io.wispforest.owo.ui.core.PositionedRectangle; +import io.wispforest.owo.ui.window.CurrentWindowContext; import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; @@ -28,7 +29,7 @@ public static void pushDirect(int x, int y, int width, int height) { push( (int) (x / scale), - (int) (window.getScaledHeight() - (y / scale) - height / scale), + (int) (CurrentWindowContext.scaledHeight() - (y / scale) - height / scale), (int) (width / scale), (int) (height / scale), null @@ -55,26 +56,24 @@ public static void pop() { STACK.pop(); applyState(); + } private static void applyState() { if (STACK.isEmpty()) { - var window = MinecraftClient.getInstance().getWindow(); - GL11.glScissor(0, 0, window.getFramebufferWidth(), window.getFramebufferHeight()); + GL11.glScissor(0, 0, CurrentWindowContext.framebufferWidth(), CurrentWindowContext.framebufferHeight()); return; } - if (!GL11.glIsEnabled(GL11.GL_SCISSOR_TEST)) return; var newFrame = STACK.peek(); - var window = MinecraftClient.getInstance().getWindow(); - var scale = window.getScaleFactor(); + var scale = CurrentWindowContext.scaleFactor(); GL11.glScissor( - (int) (newFrame.x() * scale), - (int) (window.getFramebufferHeight() - (newFrame.y() * scale) - newFrame.height() * scale), - MathHelper.clamp((int) (newFrame.width() * scale), 0, window.getFramebufferWidth()), - MathHelper.clamp((int) (newFrame.height() * scale), 0, window.getFramebufferHeight()) + (int) (newFrame.x() * scale), + (int) (CurrentWindowContext.framebufferHeight() - (newFrame.y() * scale) - newFrame.height() * scale), + MathHelper.clamp((int) (newFrame.width() * scale), 0, CurrentWindowContext.framebufferWidth()), + MathHelper.clamp((int) (newFrame.height() * scale), 0, CurrentWindowContext.framebufferHeight()) ); } diff --git a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java new file mode 100644 index 00000000..51192549 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java @@ -0,0 +1,70 @@ +package io.wispforest.owo.ui.window; + +import net.minecraft.client.MinecraftClient; + +public final class CurrentWindowContext { + private static OwoWindow CURRENT = null; + + private CurrentWindowContext() { + + } + + public static WindowResetter setCurrent(OwoWindow window) { + var old = CURRENT; + CURRENT = window; + return new WindowResetter(old); + } + + public static int framebufferWidth() { + if (CURRENT != null) { + return CURRENT.width(); + } else { + return MinecraftClient.getInstance().getWindow().getFramebufferWidth(); + } + } + + public static int scaledWidth() { + if (CURRENT != null) { + return CURRENT.scaledWidth(); + } else { + return MinecraftClient.getInstance().getWindow().getScaledWidth(); + } + } + + public static int framebufferHeight() { + if (CURRENT != null) { + return CURRENT.height(); + } else { + return MinecraftClient.getInstance().getWindow().getFramebufferHeight(); + } + } + + public static int scaledHeight() { + if (CURRENT != null) { + return CURRENT.scaledHeight(); + } else { + return MinecraftClient.getInstance().getWindow().getScaledHeight(); + } + } + + public static double scaleFactor() { + if (CURRENT != null) { + return CURRENT.scaleFactor(); + } else { + return MinecraftClient.getInstance().getWindow().getScaleFactor(); + } + } + + public static class WindowResetter implements AutoCloseable { + private final OwoWindow window; + + private WindowResetter(OwoWindow window) { + this.window = window; + } + + @Override + public void close() { + CurrentWindowContext.CURRENT = window; + } + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index 1cb9b0bb..fc1661b8 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -18,10 +18,10 @@ import static org.lwjgl.glfw.GLFW.*; public class FramebufferWindow implements AutoCloseable { - private final int width; - private final int height; + private int width; + private int height; private final long handle; - private final Framebuffer framebuffer; + private Framebuffer framebuffer; private int localFramebuffer = 0; protected final MinecraftClient client = MinecraftClient.getInstance(); @@ -30,6 +30,8 @@ public class FramebufferWindow implements AutoCloseable { private final EventStream windowClosedEvents = WindowClosed.newStream(); private final EventStream windowResizedEvents = WindowResized.newStream(); private final EventStream mouseMovedEvents = WindowMouseMoved.newStream(); + private final EventStream mouseButtonEvents = WindowMouseButton.newStream(); + private final EventStream mouseScrolledEvents = WindowMouseScrolled.newStream(); public FramebufferWindow(int width, int height, String name, long parentContext) { this.width = width; @@ -69,8 +71,12 @@ public FramebufferWindow(int width, int height, String name, long parentContext) mouseMovedEvents.sink().onMouseMoved(xpos, ypos); }))); glfwSetMouseButtonCallback(handle, stowAndReturn(GLFWMouseButtonCallback.create((window, button, action, mods) -> { -// onMouseButton.invoker().onMouseButton(button, action, mods); + mouseButtonEvents.sink().onMouseButton(button, action == GLFW_RELEASE); }))); + glfwSetScrollCallback(handle, stowAndReturn(GLFWScrollCallback.create((window, xoffset, yoffset) -> { + mouseScrolledEvents.sink().onMouseScrolled(xoffset, yoffset); + }))); + // disposeList.add(glfwSetScrollCallback(handle, (window, xoffset, yoffset) -> { // onMouseScroll.invoker().onMouseScroll(xoffset, yoffset); // })); @@ -102,6 +108,10 @@ public Framebuffer framebuffer() { return framebuffer; } + public long handle() { + return handle; + } + public EventSource windowClosed() { return windowClosedEvents.source(); } @@ -114,6 +124,14 @@ public EventSource mouseMoved() { return mouseMovedEvents.source(); } + public EventSource mouseButton() { + return mouseButtonEvents.source(); + } + + public EventSource mouseScrolled() { + return mouseScrolledEvents.source(); + } + public void present() { try (var ignored = OwoGlfwUtil.setContext(handle)) { // This code intentionally doesn't use Minecraft's RenderSystem @@ -130,23 +148,33 @@ public void present() { } private void initLocalFramebuffer() { - if (localFramebuffer != 0) { - GL32.glDeleteFramebuffers(localFramebuffer); - } + try (var ignored = OwoGlfwUtil.setContext(this.handle)) { + if (localFramebuffer != 0) { + GL32.glDeleteFramebuffers(localFramebuffer); + } - this.localFramebuffer = GL32.glGenFramebuffers(); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); - GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); + this.localFramebuffer = GL32.glGenFramebuffers(); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); - int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); - if (status != GL32.GL_FRAMEBUFFER_COMPLETE) - throw new IllegalStateException("Failed to create local framebuffer!"); + int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); + if (status != GL32.GL_FRAMEBUFFER_COMPLETE) + throw new IllegalStateException("Failed to create local framebuffer!"); + } } protected void sizeChanged(long handle, int width, int height) { if (framebuffer.viewportWidth == width && framebuffer.viewportHeight == height) return; - framebuffer.resize(width, height, MinecraftClient.IS_SYSTEM_MAC); + this.width = width; + this.height = height; + + try (var ignored = OwoGlfwUtil.setContext(client.getWindow().getHandle())) { + framebuffer.delete(); + + this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + } + initLocalFramebuffer(); windowResizedEvents.sink().onWindowResized(width, height); @@ -158,8 +186,14 @@ public boolean closed() { @Override public void close() { + try (var ignored = OwoGlfwUtil.setContext(this.handle)) { + GL32.glDeleteFramebuffers(this.localFramebuffer); + } + + this.framebuffer.delete(); + glfwDestroyWindow(this.handle); + this.disposeList.forEach(NativeResource::free); this.disposeList.clear(); - glfwDestroyWindow(this.handle); } } diff --git a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java new file mode 100644 index 00000000..d58f7944 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java @@ -0,0 +1,42 @@ +package io.wispforest.owo.ui.window; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public final class OpenWindows { + private static final List> WINDOWS = new CopyOnWriteArrayList<>(); + + private OpenWindows() { + + } + + @ApiStatus.Internal + public static WindowRegistration add(OwoWindow window) { + WINDOWS.add(window); + + return new WindowRegistration(window); + } + + @ApiStatus.Internal + public static void renderAll() { + for (OwoWindow window : WINDOWS) { + window.render(); + } + } + + public static class WindowRegistration implements AutoCloseable { + private final OwoWindow window; + + private WindowRegistration(OwoWindow window) { + this.window = window; + } + + @Override + public void close() { + WINDOWS.remove(window); + } + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 8839f066..407ddb19 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -2,7 +2,6 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.VertexSorter; -import io.wispforest.owo.Owo; import io.wispforest.owo.ui.core.OwoUIAdapter; import io.wispforest.owo.ui.core.ParentComponent; import net.minecraft.client.MinecraftClient; @@ -17,6 +16,7 @@ public abstract class OwoWindow extends FramebufferWi private int scaledWidth; private int scaledHeight; private final OwoUIAdapter adapter; + private final OpenWindows.WindowRegistration registration; private int mouseX = -1; private int mouseY = -1; @@ -29,14 +29,29 @@ public OwoWindow(int width, int height, String name, long parentContext) { build(this.adapter.rootComponent); this.adapter.inflateAndMount(); + this.registration = OpenWindows.add(this); + windowClosed().subscribe(this::close); windowResized().subscribe((newWidth, newHeight) -> { recalculateScale(); + adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); }); mouseMoved().subscribe((x, y) -> { this.mouseX = (int) (x / scaleFactor); this.mouseY = (int) (y / scaleFactor); }); + mouseButton().subscribe((button, released) -> { + if (released) { + adapter.mouseReleased(mouseX, mouseY, button); + } else { + adapter.mouseClicked(mouseX, mouseY, button); + } + }); + mouseScrolled().subscribe((xOffset, yOffset) -> { + double amount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) + * client.options.getMouseWheelSensitivity().getValue(); + adapter.mouseScrolled(mouseX, mouseY, amount); + }); } protected abstract OwoUIAdapter createAdapter(); @@ -69,33 +84,35 @@ public void recalculateScale() { } public void render() { - framebuffer().beginWrite(true); - - RenderSystem.clearColor(0, 0, 0, 1); - RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); - - Matrix4f matrix4f = new Matrix4f() - .setOrtho( - 0.0F, - scaledWidth(), - scaledHeight(), - 0.0F, - 1000.0F, - 21000.0F - ); - RenderSystem.setProjectionMatrix(matrix4f, VertexSorter.BY_Z); - MatrixStack matrixStack = RenderSystem.getModelViewStack(); - matrixStack.push(); - matrixStack.loadIdentity(); - matrixStack.translate(0.0F, 0.0F, -11000.0F); - RenderSystem.applyModelViewMatrix(); - DiffuseLighting.enableGuiDepthLighting(); - - var consumers = client.getBufferBuilders().getEntityVertexConsumers(); - adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getTickDelta()); - consumers.draw(); - - framebuffer().endWrite(); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + framebuffer().beginWrite(true); + + RenderSystem.clearColor(0, 0, 0, 1); + RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + + Matrix4f matrix4f = new Matrix4f() + .setOrtho( + 0.0F, + scaledWidth(), + scaledHeight(), + 0.0F, + 1000.0F, + 21000.0F + ); + RenderSystem.setProjectionMatrix(matrix4f, VertexSorter.BY_Z); + MatrixStack matrixStack = RenderSystem.getModelViewStack(); + matrixStack.push(); + matrixStack.loadIdentity(); + matrixStack.translate(0.0F, 0.0F, -11000.0F); + RenderSystem.applyModelViewMatrix(); + DiffuseLighting.enableGuiDepthLighting(); + + var consumers = client.getBufferBuilders().getEntityVertexConsumers(); + adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getTickDelta()); + consumers.draw(); + + framebuffer().endWrite(); + } present(); } @@ -115,6 +132,7 @@ public int scaledHeight() { @Override public void close() { adapter.dispose(); + registration.close(); super.close(); } } diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java b/src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java new file mode 100644 index 00000000..01ed3832 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.ui.window; + +import io.wispforest.owo.util.EventStream; + +public interface WindowMouseButton { + void onMouseButton(int button, boolean released); + + static EventStream newStream() { + return new EventStream<>(subscribers -> (button, released) -> { + for (var subscriber : subscribers) { + subscriber.onMouseButton(button, released); + } + }); + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java b/src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java new file mode 100644 index 00000000..2c9a3033 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.ui.window; + +import io.wispforest.owo.util.EventStream; + +public interface WindowMouseScrolled { + void onMouseScrolled(double xOffset, double yOffset); + + static EventStream newStream() { + return new EventStream<>(subscribers -> (xOffset, yOffset) -> { + for (var subscriber : subscribers) { + subscriber.onMouseScrolled(xOffset, yOffset); + } + }); + } +} diff --git a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java index ae57fe5d..3af3fa07 100644 --- a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java +++ b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java @@ -12,7 +12,10 @@ private OwoGlfwUtil() { public static ContextRestorer setContext(long handle) { long old = GLFW.glfwGetCurrentContext(); + if (old == handle) return new ContextRestorer(-1); + GLFW.glfwMakeContextCurrent(handle); + return new ContextRestorer(old); } @@ -31,6 +34,8 @@ private ContextRestorer(long old) { @Override public void close() { + if (oldContext == -1) return; + GLFW.glfwMakeContextCurrent(oldContext); } } diff --git a/src/main/java/io/wispforest/owo/util/Wisdom.java b/src/main/java/io/wispforest/owo/util/Wisdom.java index 23866b73..070d001f 100644 --- a/src/main/java/io/wispforest/owo/util/Wisdom.java +++ b/src/main/java/io/wispforest/owo/util/Wisdom.java @@ -57,8 +57,12 @@ private Wisdom() {} "that's a CanPickUpLoot baby zombie, the most annoying thing ever. he runs around like crazy and picks up all your shit" ); + public static String enlighten() { + return Util.getRandom(ALL_THE_WISDOM, CRYSTAL_BALL); + } + public static void spread() { - Owo.LOGGER.info(Util.getRandom(ALL_THE_WISDOM, CRYSTAL_BALL)); + Owo.LOGGER.info(enlighten()); } } diff --git a/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java b/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java index 13363459..d8407a80 100644 --- a/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java +++ b/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java @@ -57,6 +57,7 @@ protected void init() { .child(Components.button(Text.of("No Background"), button -> rootComponent.surface(Surface.BLANK)).margins(Insets.vertical(5)).horizontalSizing(Sizing.fixed(95))) .child(Components.button(Text.of("Dirt Background"), button -> rootComponent.surface(Surface.OPTIONS_BACKGROUND)).horizontalSizing(Sizing.fixed(95))) .child(Components.checkbox(Text.of("bruh")).onChanged(aBoolean -> this.client.player.sendMessage(Text.of("bruh: " + aBoolean))).margins(Insets.top(5))) + .child(Components.button(Text.of("bro it's a window"), button -> new UwuTestWindow()).margins(Insets.vertical(5)).horizontalSizing(Sizing.fixed(95))) .padding(Insets.of(10)) .surface(Surface.flat(0x77000000)) .positioning(Positioning.relative(1, 1)) diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuClient.java b/src/testmod/java/io/wispforest/uwu/client/UwuClient.java index f993ac5f..359f3b05 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuClient.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuClient.java @@ -18,6 +18,7 @@ import io.wispforest.uwu.network.UwuNetworkExample; import io.wispforest.uwu.network.UwuOptionalNetExample; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.MinecraftClient; @@ -149,8 +150,6 @@ public void onInitializeClient() { instance.alignComponentToHandledScreenCoordinates(button, 125, 65); }, InventoryScreen.class); - - UwuTestWindow.init(); } public record WeirdMessage(int e) {} diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index 8f0155fe..aefc6497 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -5,9 +5,9 @@ import io.wispforest.owo.ui.container.FlowLayout; import io.wispforest.owo.ui.core.Insets; import io.wispforest.owo.ui.core.OwoUIAdapter; +import io.wispforest.owo.ui.core.Sizing; import io.wispforest.owo.ui.window.OwoWindow; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import io.wispforest.owo.util.Wisdom; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; @@ -16,27 +16,22 @@ public UwuTestWindow() { super(640, 480, "uωu test window!", MinecraftClient.getInstance().getWindow().getHandle()); } - public static void init() { - ClientLifecycleEvents.CLIENT_STARTED.register(client -> { - var window = new UwuTestWindow(); - - ClientTickEvents.END_CLIENT_TICK.register(client1 -> { - if (window.closed()) return; - - window.render(); - }); - }); - } - @Override protected OwoUIAdapter createAdapter() { - return OwoUIAdapter.createWithoutScreen(0, 0, scaledWidth(), scaledHeight(), Containers::verticalFlow); + return OwoUIAdapter.create(this, Containers::verticalFlow); } @Override protected void build(FlowLayout rootComponent) { - rootComponent.margins(Insets.of(10)); - rootComponent.child(Components.label(Text.literal("Honestly quite shrimple"))); - rootComponent.child(Components.button(Text.literal("Honestly quite shrimple"), unused -> {})); + rootComponent.padding(Insets.of(10)); + + var inner = Containers.verticalFlow(Sizing.content(), Sizing.content()); + rootComponent.child(Containers.verticalScroll(Sizing.content(), Sizing.fill(100), inner)); + + inner + .child(Components.button(Text.literal("Honestly quite shrimple"), unused -> { + inner.child(Components.label( + Text.literal(Wisdom.enlighten()))); + })); } } From beb097081d9eabb3f79a3687e157435ac71fdb93 Mon Sep 17 00:00:00 2001 From: Basique Date: Mon, 31 Jul 2023 06:39:52 +0300 Subject: [PATCH 03/20] thank you intellij --- .../java/io/wispforest/owo/ui/window/OpenWindows.java | 1 - src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java | 9 --------- .../io/wispforest/uwu/client/ComponentTestScreen.java | 1 - src/testmod/java/io/wispforest/uwu/client/UwuClient.java | 1 - 4 files changed, 12 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java index d58f7944..9e521d00 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java +++ b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java @@ -2,7 +2,6 @@ import org.jetbrains.annotations.ApiStatus; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java index 3af3fa07..e2ac652f 100644 --- a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java +++ b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java @@ -1,9 +1,6 @@ package io.wispforest.owo.util; -import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFW; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.system.MemoryUtil; public final class OwoGlfwUtil { private OwoGlfwUtil() { @@ -19,12 +16,6 @@ public static ContextRestorer setContext(long handle) { return new ContextRestorer(old); } - public static class GlfwException extends RuntimeException { - public GlfwException(String message) { - super(message); - } - } - public static class ContextRestorer implements AutoCloseable { private final long oldContext; diff --git a/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java b/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java index d8407a80..8bf85db7 100644 --- a/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java +++ b/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java @@ -15,7 +15,6 @@ import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.texture.SpriteAtlasTexture; import net.minecraft.client.util.SpriteIdentifier; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuClient.java b/src/testmod/java/io/wispforest/uwu/client/UwuClient.java index 359f3b05..646c91b2 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuClient.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuClient.java @@ -18,7 +18,6 @@ import io.wispforest.uwu.network.UwuNetworkExample; import io.wispforest.uwu.network.UwuOptionalNetExample; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.MinecraftClient; From 989a1e76c2a16ec45d344b1eafa733db83fcf963 Mon Sep 17 00:00:00 2001 From: Basique Date: Mon, 31 Jul 2023 12:51:23 +0300 Subject: [PATCH 04/20] ae --- .../owo/ui/window/FramebufferWindow.java | 30 +++++++++++-------- .../wispforest/owo/ui/window/OwoWindow.java | 6 ++++ .../owo/ui/window/WindowKeyPressed.java | 15 ++++++++++ .../wispforest/uwu/client/UwuTestWindow.java | 24 +++++++++++---- 4 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index fc1661b8..d41d9983 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -1,6 +1,7 @@ package io.wispforest.owo.ui.window; import com.mojang.blaze3d.systems.RenderSystem; +import io.wispforest.owo.ui.event.CharTyped; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.EventStream; import io.wispforest.owo.util.OwoGlfwUtil; @@ -32,6 +33,8 @@ public class FramebufferWindow implements AutoCloseable { private final EventStream mouseMovedEvents = WindowMouseMoved.newStream(); private final EventStream mouseButtonEvents = WindowMouseButton.newStream(); private final EventStream mouseScrolledEvents = WindowMouseScrolled.newStream(); + private final EventStream keyPressedEvents = WindowKeyPressed.newStream(); + private final EventStream charTypedEvents = CharTyped.newStream(); public FramebufferWindow(int width, int height, String name, long parentContext) { this.width = width; @@ -76,19 +79,12 @@ public FramebufferWindow(int width, int height, String name, long parentContext) glfwSetScrollCallback(handle, stowAndReturn(GLFWScrollCallback.create((window, xoffset, yoffset) -> { mouseScrolledEvents.sink().onMouseScrolled(xoffset, yoffset); }))); - -// disposeList.add(glfwSetScrollCallback(handle, (window, xoffset, yoffset) -> { -// onMouseScroll.invoker().onMouseScroll(xoffset, yoffset); -// })); -// disposeList.add(glfwSetDropCallback(handle, (window, count, names) -> { -// Path[] paths = new Path[count]; -// -// for (int j = 0; j < count; ++j) { -// paths[j] = Paths.get(GLFWDropCallback.getName(names, j)); -// } -// -// onFilesDropped.invoker().onFilesDropped(paths); -// })); + glfwSetKeyCallback(handle, stowAndReturn(GLFWKeyCallback.create((window, key, scancode, action, mods) -> { + keyPressedEvents.sink().onKeyPressed(key, scancode, mods, action == GLFW_RELEASE); + }))); + glfwSetCharModsCallback(handle, stowAndReturn(GLFWCharModsCallback.create((window, codepoint, mods) -> { + charTypedEvents.sink().onCharTyped((char) codepoint, mods); + }))); } private T stowAndReturn(T resource) { @@ -132,6 +128,14 @@ public EventSource mouseScrolled() { return mouseScrolledEvents.source(); } + public EventSource keyPressed() { + return keyPressedEvents.source(); + } + + public EventSource charTyped() { + return charTypedEvents.source(); + } + public void present() { try (var ignored = OwoGlfwUtil.setContext(handle)) { // This code intentionally doesn't use Minecraft's RenderSystem diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 407ddb19..47b05c79 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -52,6 +52,12 @@ public OwoWindow(int width, int height, String name, long parentContext) { * client.options.getMouseWheelSensitivity().getValue(); adapter.mouseScrolled(mouseX, mouseY, amount); }); + keyPressed().subscribe((keyCode, scanCode, modifiers, released) -> { + if (released) return; + + adapter.keyPressed(keyCode, scanCode, modifiers); + }); + charTyped().subscribe(adapter::charTyped); } protected abstract OwoUIAdapter createAdapter(); diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java b/src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java new file mode 100644 index 00000000..4dd64189 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.ui.window; + +import io.wispforest.owo.util.EventStream; + +public interface WindowKeyPressed { + void onKeyPressed(int keyCode, int scanCode, int modifiers, boolean released); + + static EventStream newStream() { + return new EventStream<>(subscribers -> (keyCode, scanCode, modifiers, released) -> { + for (var subscriber : subscribers) { + subscriber.onKeyPressed(keyCode, scanCode, modifiers, released); + } + }); + } +} diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index aefc6497..42b64e4e 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -7,9 +7,9 @@ import io.wispforest.owo.ui.core.OwoUIAdapter; import io.wispforest.owo.ui.core.Sizing; import io.wispforest.owo.ui.window.OwoWindow; -import io.wispforest.owo.util.Wisdom; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; public class UwuTestWindow extends OwoWindow { public UwuTestWindow() { @@ -28,10 +28,24 @@ protected void build(FlowLayout rootComponent) { var inner = Containers.verticalFlow(Sizing.content(), Sizing.content()); rootComponent.child(Containers.verticalScroll(Sizing.content(), Sizing.fill(100), inner)); + inner.child(Components.label(Text.of("Are you an owl?"))); + + var textbox = Components.textBox(Sizing.fixed(60)); + var statusLabel = Components.label(Text.empty()); + + textbox.onChanged().subscribe(value -> { + if (value.equalsIgnoreCase("yes")) { + statusLabel.text(Text.literal("Owl :)") + .formatted(Formatting.GREEN)); + } else { + statusLabel.text(Text.literal("Not an owl :(") + .formatted(Formatting.RED)); + } + }); + inner - .child(Components.button(Text.literal("Honestly quite shrimple"), unused -> { - inner.child(Components.label( - Text.literal(Wisdom.enlighten()))); - })); + .child(textbox + .margins(Insets.vertical(5))) + .child(statusLabel); } } From b6f6a9dc68d16ed7e28708311cad61972064f26c Mon Sep 17 00:00:00 2001 From: Basique Date: Fri, 4 Aug 2023 17:16:22 +0300 Subject: [PATCH 05/20] fix up screen methods to use current window --- .../wispforest/owo/mixin/ui/ScreenMixin.java | 6 ++++ .../owo/ui/window/CurrentWindowContext.java | 12 +++++++ .../wispforest/owo/ui/window/OwoWindow.java | 36 +++++++++++++------ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java b/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java index 944fe44f..49bd30c5 100644 --- a/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java +++ b/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java @@ -4,6 +4,7 @@ import io.wispforest.owo.ui.base.BaseOwoHandledScreen; import io.wispforest.owo.ui.base.BaseOwoScreen; import io.wispforest.owo.ui.core.OwoUIDrawContext; +import io.wispforest.owo.ui.window.CurrentWindowContext; import net.minecraft.client.gui.screen.Screen; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -28,4 +29,9 @@ private boolean dontCloseOwoScreens(boolean original) { if ((Object) this != OwoUIDrawContext.utilityScreen()) return screen; return OwoUIDrawContext.utilityScreen().getAndClearLinkSource(); } + + @ModifyArg(method = {"hasShiftDown", "hasControlDown", "hasAltDown"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/InputUtil;isKeyPressed(JI)Z"), index = 0) + private static long replaceOnOtherWindow(long handle) { + return CurrentWindowContext.isMain() ? handle : CurrentWindowContext.handle(); + } } diff --git a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java index 51192549..a8ed4fd7 100644 --- a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java @@ -15,6 +15,10 @@ public static WindowResetter setCurrent(OwoWindow window) { return new WindowResetter(old); } + public static boolean isMain() { + return CURRENT == null; + } + public static int framebufferWidth() { if (CURRENT != null) { return CURRENT.width(); @@ -55,6 +59,14 @@ public static double scaleFactor() { } } + public static long handle() { + if (CURRENT != null) { + return CURRENT.handle(); + } else { + return MinecraftClient.getInstance().getWindow().getHandle(); + } + } + public static class WindowResetter implements AutoCloseable { private final OwoWindow window; diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 47b05c79..f5bfc648 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -33,31 +33,43 @@ public OwoWindow(int width, int height, String name, long parentContext) { windowClosed().subscribe(this::close); windowResized().subscribe((newWidth, newHeight) -> { - recalculateScale(); - adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + recalculateScale(); + adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); + } }); mouseMoved().subscribe((x, y) -> { this.mouseX = (int) (x / scaleFactor); this.mouseY = (int) (y / scaleFactor); }); mouseButton().subscribe((button, released) -> { - if (released) { - adapter.mouseReleased(mouseX, mouseY, button); - } else { - adapter.mouseClicked(mouseX, mouseY, button); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + if (released) { + adapter.mouseReleased(mouseX, mouseY, button); + } else { + adapter.mouseClicked(mouseX, mouseY, button); + } } }); mouseScrolled().subscribe((xOffset, yOffset) -> { - double amount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) + try (var ignored = CurrentWindowContext.setCurrent(this)) { + double amount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) * client.options.getMouseWheelSensitivity().getValue(); - adapter.mouseScrolled(mouseX, mouseY, amount); + adapter.mouseScrolled(mouseX, mouseY, amount); + } }); keyPressed().subscribe((keyCode, scanCode, modifiers, released) -> { if (released) return; - adapter.keyPressed(keyCode, scanCode, modifiers); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + adapter.keyPressed(keyCode, scanCode, modifiers); + } + }); + charTyped().subscribe((chr, modifiers) -> { + try (var ignored = CurrentWindowContext.setCurrent(this)) { + return adapter.charTyped(chr, modifiers); + } }); - charTyped().subscribe(adapter::charTyped); } protected abstract OwoUIAdapter createAdapter(); @@ -105,6 +117,7 @@ public void render() { 1000.0F, 21000.0F ); + RenderSystem.backupProjectionMatrix(); RenderSystem.setProjectionMatrix(matrix4f, VertexSorter.BY_Z); MatrixStack matrixStack = RenderSystem.getModelViewStack(); matrixStack.push(); @@ -117,6 +130,9 @@ public void render() { adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getTickDelta()); consumers.draw(); + RenderSystem.getModelViewStack().pop(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.restoreProjectionMatrix(); framebuffer().endWrite(); } From e4df2c36f7563b3747f4a791c1998746b016bdb6 Mon Sep 17 00:00:00 2001 From: Basique Date: Fri, 4 Aug 2023 17:53:04 +0300 Subject: [PATCH 06/20] make more stuff private --- .../wispforest/owo/ui/window/CurrentWindowContext.java | 2 +- .../java/io/wispforest/owo/ui/window/OpenWindows.java | 10 ++++++++-- .../java/io/wispforest/owo/ui/window/OwoWindow.java | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java index a8ed4fd7..c0990acd 100644 --- a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java @@ -9,7 +9,7 @@ private CurrentWindowContext() { } - public static WindowResetter setCurrent(OwoWindow window) { + static WindowResetter setCurrent(OwoWindow window) { var old = CURRENT; CURRENT = window; return new WindowResetter(old); diff --git a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java index 9e521d00..ab2b0728 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java +++ b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java @@ -1,24 +1,30 @@ package io.wispforest.owo.ui.window; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.UnmodifiableView; +import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public final class OpenWindows { private static final List> WINDOWS = new CopyOnWriteArrayList<>(); + private static final List> WINDOWS_VIEW = Collections.unmodifiableList(WINDOWS); private OpenWindows() { } - @ApiStatus.Internal - public static WindowRegistration add(OwoWindow window) { + static WindowRegistration add(OwoWindow window) { WINDOWS.add(window); return new WindowRegistration(window); } + public static @UnmodifiableView List> windows() { + return WINDOWS_VIEW; + } + @ApiStatus.Internal public static void renderAll() { for (OwoWindow window : WINDOWS) { diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index f5bfc648..5dc5558a 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -102,6 +102,8 @@ public void recalculateScale() { } public void render() { + if (closed()) return; + try (var ignored = CurrentWindowContext.setCurrent(this)) { framebuffer().beginWrite(true); From e6435ef6f09c6a4c5267ee0d148e2ccf1cab3d5b Mon Sep 17 00:00:00 2001 From: Basique Date: Fri, 4 Aug 2023 20:10:42 +0300 Subject: [PATCH 07/20] fix more bugs --- .../wispforest/owo/ui/window/FramebufferWindow.java | 13 ++++++++++++- .../java/io/wispforest/owo/ui/window/OwoWindow.java | 8 +++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index d41d9983..9bc028e9 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -9,6 +9,7 @@ import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.GlDebug; import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.render.Tessellator; import org.lwjgl.glfw.*; import org.lwjgl.opengl.GL32; import org.lwjgl.system.NativeResource; @@ -37,6 +38,10 @@ public class FramebufferWindow implements AutoCloseable { private final EventStream charTypedEvents = CharTyped.newStream(); public FramebufferWindow(int width, int height, String name, long parentContext) { + if (glfwGetCurrentContext() != MinecraftClient.getInstance().getWindow().getHandle()) { + throw new IllegalStateException("Window was created on alternate GL context"); + } + this.width = width; this.height = height; @@ -55,6 +60,7 @@ public FramebufferWindow(int width, int height, String name, long parentContext) } glfwMakeContextCurrent(handle); + glfwSwapInterval(0); } this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); @@ -137,6 +143,8 @@ public EventSource charTyped() { } public void present() { + if (closed()) return; + try (var ignored = OwoGlfwUtil.setContext(handle)) { // This code intentionally doesn't use Minecraft's RenderSystem // class, as it caches GL state that is invalid on this context. @@ -147,7 +155,10 @@ public void present() { GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); GL32.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL32.GL_COLOR_BUFFER_BIT, GL32.GL_NEAREST); - RenderSystem.flipFrame(handle); + // Intentionally doesn't poll events so that all events are on the main window + RenderSystem.replayQueue(); + Tessellator.getInstance().getBuffer().clear(); + GLFW.glfwSwapBuffers(this.handle); } } diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 5dc5558a..e681bccc 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -25,9 +25,11 @@ public OwoWindow(int width, int height, String name, long parentContext) { super(width, height, name, parentContext); recalculateScale(); - this.adapter = createAdapter(); - build(this.adapter.rootComponent); - this.adapter.inflateAndMount(); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + this.adapter = createAdapter(); + build(this.adapter.rootComponent); + this.adapter.inflateAndMount(); + } this.registration = OpenWindows.add(this); From 4a72313a9cadd6de28becbc30e6ba50bccd043fd Mon Sep 17 00:00:00 2001 From: Basique Date: Sun, 20 Aug 2023 16:50:18 +0300 Subject: [PATCH 08/20] commit so that I can resume working on my desktop --- .../wispforest/owo/mixin/ui/ScreenMixin.java | 8 +- .../wispforest/owo/ui/core/OwoUIAdapter.java | 4 +- .../wispforest/owo/ui/util/ScissorStack.java | 22 ++--- .../owo/ui/window/CurrentWindowContext.java | 82 ------------------- .../owo/ui/window/FramebufferWindow.java | 38 +++++++-- .../wispforest/owo/ui/window/OwoWindow.java | 20 +++-- ...zed.java => WindowFramebufferResized.java} | 8 +- .../window/context/CurrentWindowContext.java | 34 ++++++++ .../window/context/VanillaWindowContext.java | 61 ++++++++++++++ .../owo/ui/window/context/WindowContext.java | 16 ++++ 10 files changed, 176 insertions(+), 117 deletions(-) delete mode 100644 src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java rename src/main/java/io/wispforest/owo/ui/window/{WindowResized.java => WindowFramebufferResized.java} (50%) create mode 100644 src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java diff --git a/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java b/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java index 49bd30c5..0f63613d 100644 --- a/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java +++ b/src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java @@ -4,7 +4,9 @@ import io.wispforest.owo.ui.base.BaseOwoHandledScreen; import io.wispforest.owo.ui.base.BaseOwoScreen; import io.wispforest.owo.ui.core.OwoUIDrawContext; -import io.wispforest.owo.ui.window.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.VanillaWindowContext; +import io.wispforest.owo.ui.window.context.WindowContext; import net.minecraft.client.gui.screen.Screen; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -32,6 +34,8 @@ private boolean dontCloseOwoScreens(boolean original) { @ModifyArg(method = {"hasShiftDown", "hasControlDown", "hasAltDown"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/InputUtil;isKeyPressed(JI)Z"), index = 0) private static long replaceOnOtherWindow(long handle) { - return CurrentWindowContext.isMain() ? handle : CurrentWindowContext.handle(); + WindowContext ctx = CurrentWindowContext.current(); + + return ctx != VanillaWindowContext.MAIN ? ctx.handle() : handle; } } diff --git a/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java b/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java index 53c37f26..ca0d3359 100644 --- a/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java +++ b/src/main/java/io/wispforest/owo/ui/core/OwoUIAdapter.java @@ -5,7 +5,7 @@ import io.wispforest.owo.Owo; import io.wispforest.owo.renderdoc.RenderDoc; import io.wispforest.owo.ui.util.CursorAdapter; -import io.wispforest.owo.ui.window.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.ui.window.OwoWindow; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; @@ -190,7 +190,7 @@ public void render(DrawContext context, int mouseX, int mouseY, float partialTic RenderSystem.enableDepthTest(); GlStateManager._enableScissorTest(); - GlStateManager._scissorBox(0, 0, CurrentWindowContext.framebufferWidth(), CurrentWindowContext.framebufferHeight()); + GlStateManager._scissorBox(0, 0, CurrentWindowContext.current().framebufferWidth(), CurrentWindowContext.current().framebufferHeight()); this.rootComponent.draw(owoContext, mouseX, mouseY, partialTicks, delta); GlStateManager._disableScissorTest(); diff --git a/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java b/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java index f52a89af..018e1eec 100644 --- a/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java +++ b/src/main/java/io/wispforest/owo/ui/util/ScissorStack.java @@ -4,8 +4,8 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.ui.core.Component; import io.wispforest.owo.ui.core.PositionedRectangle; -import io.wispforest.owo.ui.window.CurrentWindowContext; -import net.minecraft.client.MinecraftClient; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.WindowContext; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; import org.jetbrains.annotations.Nullable; @@ -24,12 +24,12 @@ public final class ScissorStack { private ScissorStack() {} public static void pushDirect(int x, int y, int width, int height) { - var window = MinecraftClient.getInstance().getWindow(); - var scale = window.getScaleFactor(); + var ctx = CurrentWindowContext.current(); + var scale = ctx.scaleFactor(); push( (int) (x / scale), - (int) (CurrentWindowContext.scaledHeight() - (y / scale) - height / scale), + (int) (ctx.scaledHeight() - (y / scale) - height / scale), (int) (width / scale), (int) (height / scale), null @@ -60,20 +60,22 @@ public static void pop() { } private static void applyState() { + WindowContext ctx = CurrentWindowContext.current(); + if (STACK.isEmpty()) { - GL11.glScissor(0, 0, CurrentWindowContext.framebufferWidth(), CurrentWindowContext.framebufferHeight()); + GL11.glScissor(0, 0, ctx.framebufferWidth(), ctx.framebufferHeight()); return; } if (!GL11.glIsEnabled(GL11.GL_SCISSOR_TEST)) return; var newFrame = STACK.peek(); - var scale = CurrentWindowContext.scaleFactor(); + var scale = ctx.scaleFactor(); GL11.glScissor( (int) (newFrame.x() * scale), - (int) (CurrentWindowContext.framebufferHeight() - (newFrame.y() * scale) - newFrame.height() * scale), - MathHelper.clamp((int) (newFrame.width() * scale), 0, CurrentWindowContext.framebufferWidth()), - MathHelper.clamp((int) (newFrame.height() * scale), 0, CurrentWindowContext.framebufferHeight()) + (int) (ctx.framebufferHeight() - (newFrame.y() * scale) - newFrame.height() * scale), + MathHelper.clamp((int) (newFrame.width() * scale), 0, ctx.framebufferWidth()), + MathHelper.clamp((int) (newFrame.height() * scale), 0, ctx.framebufferHeight()) ); } diff --git a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java deleted file mode 100644 index c0990acd..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/CurrentWindowContext.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.wispforest.owo.ui.window; - -import net.minecraft.client.MinecraftClient; - -public final class CurrentWindowContext { - private static OwoWindow CURRENT = null; - - private CurrentWindowContext() { - - } - - static WindowResetter setCurrent(OwoWindow window) { - var old = CURRENT; - CURRENT = window; - return new WindowResetter(old); - } - - public static boolean isMain() { - return CURRENT == null; - } - - public static int framebufferWidth() { - if (CURRENT != null) { - return CURRENT.width(); - } else { - return MinecraftClient.getInstance().getWindow().getFramebufferWidth(); - } - } - - public static int scaledWidth() { - if (CURRENT != null) { - return CURRENT.scaledWidth(); - } else { - return MinecraftClient.getInstance().getWindow().getScaledWidth(); - } - } - - public static int framebufferHeight() { - if (CURRENT != null) { - return CURRENT.height(); - } else { - return MinecraftClient.getInstance().getWindow().getFramebufferHeight(); - } - } - - public static int scaledHeight() { - if (CURRENT != null) { - return CURRENT.scaledHeight(); - } else { - return MinecraftClient.getInstance().getWindow().getScaledHeight(); - } - } - - public static double scaleFactor() { - if (CURRENT != null) { - return CURRENT.scaleFactor(); - } else { - return MinecraftClient.getInstance().getWindow().getScaleFactor(); - } - } - - public static long handle() { - if (CURRENT != null) { - return CURRENT.handle(); - } else { - return MinecraftClient.getInstance().getWindow().getHandle(); - } - } - - public static class WindowResetter implements AutoCloseable { - private final OwoWindow window; - - private WindowResetter(OwoWindow window) { - this.window = window; - } - - @Override - public void close() { - CurrentWindowContext.CURRENT = window; - } - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index 9bc028e9..892a2e69 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.ui.event.CharTyped; +import io.wispforest.owo.ui.window.context.WindowContext; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.EventStream; import io.wispforest.owo.util.OwoGlfwUtil; @@ -19,7 +20,7 @@ import static org.lwjgl.glfw.GLFW.*; -public class FramebufferWindow implements AutoCloseable { +public class FramebufferWindow implements AutoCloseable, WindowContext { private int width; private int height; private final long handle; @@ -30,7 +31,7 @@ public class FramebufferWindow implements AutoCloseable { private final List disposeList; private final EventStream windowClosedEvents = WindowClosed.newStream(); - private final EventStream windowResizedEvents = WindowResized.newStream(); + private final EventStream framebufferResizedEvents = WindowFramebufferResized.newStream(); private final EventStream mouseMovedEvents = WindowMouseMoved.newStream(); private final EventStream mouseButtonEvents = WindowMouseButton.newStream(); private final EventStream mouseScrolledEvents = WindowMouseScrolled.newStream(); @@ -98,18 +99,36 @@ private T stowAndReturn(T resource) { return resource; } - public int width() { + public Framebuffer framebuffer() { + return framebuffer; + } + + @Override + public int framebufferWidth() { return width; } - public int height() { + @Override + public int framebufferHeight() { return height; } - public Framebuffer framebuffer() { - return framebuffer; + @Override + public int scaledWidth() { + return framebufferWidth(); } + @Override + public int scaledHeight() { + return framebufferHeight(); + } + + @Override + public double scaleFactor() { + return 1; + } + + @Override public long handle() { return handle; } @@ -118,8 +137,9 @@ public EventSource windowClosed() { return windowClosedEvents.source(); } - public EventSource windowResized() { - return windowResizedEvents.source(); + @Override + public EventSource framebufferResized() { + return framebufferResizedEvents.source(); } public EventSource mouseMoved() { @@ -192,7 +212,7 @@ protected void sizeChanged(long handle, int width, int height) { initLocalFramebuffer(); - windowResizedEvents.sink().onWindowResized(width, height); + framebufferResizedEvents.sink().onFramebufferResized(width, height); } public boolean closed() { diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index e681bccc..f10a8826 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.systems.VertexSorter; import io.wispforest.owo.ui.core.OwoUIAdapter; import io.wispforest.owo.ui.core.ParentComponent; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.render.DiffuseLighting; @@ -34,7 +35,7 @@ public OwoWindow(int width, int height, String name, long parentContext) { this.registration = OpenWindows.add(this); windowClosed().subscribe(this::close); - windowResized().subscribe((newWidth, newHeight) -> { + framebufferResized().subscribe((newWidth, newHeight) -> { try (var ignored = CurrentWindowContext.setCurrent(this)) { recalculateScale(); adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); @@ -86,10 +87,10 @@ public void recalculateScale() { while ( factor != guiScale - && factor < this.width() - && factor < this.height() - && this.width() / (factor + 1) >= 320 - && this.height() / (factor + 1) >= 240 + && factor < this.framebufferWidth() + && factor < this.framebufferHeight() + && this.framebufferWidth() / (factor + 1) >= 320 + && this.framebufferHeight() / (factor + 1) >= 240 ) { ++factor; } @@ -99,8 +100,8 @@ public void recalculateScale() { } this.scaleFactor = factor; - this.scaledWidth = (int) Math.ceil((double) this.width() / scaleFactor); - this.scaledHeight = (int) Math.ceil((double) this.height() / scaleFactor); + this.scaledWidth = (int) Math.ceil((double) this.framebufferWidth() / scaleFactor); + this.scaledHeight = (int) Math.ceil((double) this.framebufferHeight() / scaleFactor); } public void render() { @@ -143,14 +144,17 @@ public void render() { present(); } - public int scaleFactor() { + @Override + public double scaleFactor() { return scaleFactor; } + @Override public int scaledWidth() { return scaledWidth; } + @Override public int scaledHeight() { return scaledHeight; } diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowResized.java b/src/main/java/io/wispforest/owo/ui/window/WindowFramebufferResized.java similarity index 50% rename from src/main/java/io/wispforest/owo/ui/window/WindowResized.java rename to src/main/java/io/wispforest/owo/ui/window/WindowFramebufferResized.java index c0505ca2..ac981736 100644 --- a/src/main/java/io/wispforest/owo/ui/window/WindowResized.java +++ b/src/main/java/io/wispforest/owo/ui/window/WindowFramebufferResized.java @@ -2,13 +2,13 @@ import io.wispforest.owo.util.EventStream; -public interface WindowResized { - void onWindowResized(int newWidth, int newHeight); +public interface WindowFramebufferResized { + void onFramebufferResized(int newWidth, int newHeight); - static EventStream newStream() { + static EventStream newStream() { return new EventStream<>(subscribers -> (newWidth, newHeight) -> { for (var subscriber : subscribers) { - subscriber.onWindowResized(newWidth, newHeight); + subscriber.onFramebufferResized(newWidth, newHeight); } }); } diff --git a/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java new file mode 100644 index 00000000..ecff88e2 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java @@ -0,0 +1,34 @@ +package io.wispforest.owo.ui.window.context; + +import net.minecraft.client.MinecraftClient; + +public final class CurrentWindowContext { + private static WindowContext CURRENT = VanillaWindowContext.MAIN; + + private CurrentWindowContext() { + + } + + public static WindowResetter setCurrent(WindowContext window) { + var old = CURRENT; + CURRENT = window; + return new WindowResetter(old); + } + + public static WindowContext current() { + return CURRENT; + } + + public static class WindowResetter implements AutoCloseable { + private final WindowContext window; + + private WindowResetter(WindowContext window) { + this.window = window; + } + + @Override + public void close() { + CurrentWindowContext.CURRENT = window; + } + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java new file mode 100644 index 00000000..7bfadbc8 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java @@ -0,0 +1,61 @@ +package io.wispforest.owo.ui.window.context; + +import io.wispforest.owo.ui.event.WindowResizeCallback; +import io.wispforest.owo.ui.window.WindowFramebufferResized; +import io.wispforest.owo.util.EventSource; +import io.wispforest.owo.util.EventStream; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.util.Window; + +public class VanillaWindowContext implements WindowContext { + public static final VanillaWindowContext MAIN = new VanillaWindowContext(MinecraftClient.getInstance().getWindow()); + + private final Window window; + + private final EventStream framebufferResizedEvents = WindowFramebufferResized.newStream(); + + public VanillaWindowContext(Window window) { + this.window = window; + + WindowResizeCallback.EVENT.register((client, window1) -> { + if (window != window1) return; + + framebufferResizedEvents.sink().onFramebufferResized(window1.getFramebufferWidth(), window1.getFramebufferHeight()); + }); + } + + @Override + public int framebufferWidth() { + return window.getFramebufferWidth(); + } + + @Override + public int framebufferHeight() { + return window.getFramebufferHeight(); + } + + @Override + public EventSource framebufferResized() { + return framebufferResizedEvents.source(); + } + + @Override + public int scaledWidth() { + return window.getScaledWidth(); + } + + @Override + public int scaledHeight() { + return window.getScaledHeight(); + } + + @Override + public double scaleFactor() { + return window.getScaleFactor(); + } + + @Override + public long handle() { + return window.getHandle(); + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java new file mode 100644 index 00000000..3a7b6ac6 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java @@ -0,0 +1,16 @@ +package io.wispforest.owo.ui.window.context; + +import io.wispforest.owo.ui.window.WindowFramebufferResized; +import io.wispforest.owo.util.EventSource; + +public interface WindowContext { + int framebufferWidth(); + int framebufferHeight(); + EventSource framebufferResized(); + + int scaledWidth(); + int scaledHeight(); + double scaleFactor(); + + long handle(); +} From 7c224ee9df0f50b23b557926840adcc3e63edccd Mon Sep 17 00:00:00 2001 From: Basique Date: Sun, 20 Aug 2023 20:05:49 +0300 Subject: [PATCH 09/20] make render effect wrappers work, generify some window context stuff --- .../owo/ui/container/RenderEffectWrapper.java | 54 ++++++++++++++----- .../owo/ui/core/OwoUIDrawContext.java | 31 ++++------- .../owo/ui/window/FramebufferWindow.java | 30 ++++++++--- .../wispforest/owo/ui/window/OwoWindow.java | 32 ++++------- .../window/context/CurrentWindowContext.java | 2 - .../window/context/VanillaWindowContext.java | 3 +- .../owo/ui/window/context/WindowContext.java | 3 +- .../wispforest/owo/util/SupportsFeatures.java | 9 ++++ .../owo/util/SupportsFeaturesImpl.java | 34 ++++++++++++ .../wispforest/uwu/client/UwuTestWindow.java | 9 ++++ 10 files changed, 140 insertions(+), 67 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/util/SupportsFeatures.java create mode 100644 src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java diff --git a/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java b/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java index c037c1e5..fe3b172d 100644 --- a/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java +++ b/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java @@ -8,6 +8,9 @@ import io.wispforest.owo.ui.core.Sizing; import io.wispforest.owo.ui.event.WindowResizeCallback; import io.wispforest.owo.ui.util.ScissorStack; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.WindowContext; +import io.wispforest.owo.util.SupportsFeatures; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.SimpleFramebuffer; @@ -24,6 +27,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; /** @@ -41,7 +45,6 @@ @ApiStatus.Experimental public class RenderEffectWrapper extends WrappingParentComponent { - protected static final List FRAMEBUFFERS = new ArrayList<>(); protected static int drawDepth = 0; protected final List effects = new ArrayList<>(); @@ -58,13 +61,11 @@ public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partial try { drawDepth++; - var window = MinecraftClient.getInstance().getWindow(); - while (drawDepth > FRAMEBUFFERS.size()) { - FRAMEBUFFERS.add(new SimpleFramebuffer(window.getFramebufferWidth(), window.getFramebufferHeight(), true, MinecraftClient.IS_SYSTEM_MAC)); - } + var window = CurrentWindowContext.current(); + var feature = window.get(FramebuffersFeature.KEY); var previousFramebuffer = GlStateManager.getBoundFramebuffer(); - var framebuffer = FRAMEBUFFERS.get(drawDepth - 1); + var framebuffer = feature.getFor(drawDepth); framebuffer.setClearColor(0, 0, 0, 0); ScissorStack.drawUnclipped(() -> framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC)); framebuffer.beginWrite(false); @@ -82,9 +83,9 @@ public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partial var matrix = context.getMatrices().peek().getPositionMatrix(); buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); - buffer.vertex(matrix, 0, window.getScaledHeight(), 0).texture(0, 0).color(1f, 1f, 1f, 1f).next(); - buffer.vertex(matrix, window.getScaledWidth(), window.getScaledHeight(), 0).texture(1, 0).color(1f, 1f, 1f, 1f).next(); - buffer.vertex(matrix, window.getScaledWidth(), 0, 0).texture(1, 1).color(1f, 1f, 1f, 1f).next(); + buffer.vertex(matrix, 0, window.scaledHeight(), 0).texture(0, 0).color(1f, 1f, 1f, 1f).next(); + buffer.vertex(matrix, window.scaledWidth(), window.scaledHeight(), 0).texture(1, 0).color(1f, 1f, 1f, 1f).next(); + buffer.vertex(matrix, window.scaledWidth(), 0, 0).texture(1, 1).color(1f, 1f, 1f, 1f).next(); buffer.vertex(matrix, 0, 0, 0).texture(0, 1).color(1f, 1f, 1f, 1f).next(); RenderSystem.setShaderTexture(0, framebuffer.getColorAttachment()); @@ -122,12 +123,37 @@ public void clearEffects() { this.effects.clear(); } - static { - WindowResizeCallback.EVENT.register((client, window) -> { - FRAMEBUFFERS.forEach(framebuffer -> { - framebuffer.resize(window.getFramebufferWidth(), window.getFramebufferHeight(), MinecraftClient.IS_SYSTEM_MAC); + private static final class FramebuffersFeature implements AutoCloseable { + public static SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(FramebuffersFeature::new); + + private final WindowContext ctx; + private final List framebuffers; + + public FramebuffersFeature(WindowContext ctx) { + this.framebuffers = new ArrayList<>(); + this.ctx = ctx; + + ctx.framebufferResized().subscribe((newWidth, newHeight) -> { + framebuffers.forEach(framebuffer -> { + framebuffer.resize(newWidth, newHeight, MinecraftClient.IS_SYSTEM_MAC); + }); }); - }); + } + + public Framebuffer getFor(int drawDepth) { + while (drawDepth > framebuffers.size()) { + framebuffers.add(new SimpleFramebuffer(ctx.framebufferWidth(), ctx.framebufferHeight(), true, MinecraftClient.IS_SYSTEM_MAC)); + } + + return framebuffers.get(drawDepth - 1); + } + + @Override + public void close() { + for (Framebuffer fb : framebuffers) { + fb.delete(); + } + } } public class RenderEffectSlot { diff --git a/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java b/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java index 7892b86a..5f1d2386 100644 --- a/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java +++ b/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java @@ -6,6 +6,9 @@ import io.wispforest.owo.mixin.ui.DrawContextInvoker; import io.wispforest.owo.ui.event.WindowResizeCallback; import io.wispforest.owo.ui.util.NinePatchTexture; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.WindowContext; +import io.wispforest.owo.util.SupportsFeatures; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; @@ -347,27 +350,20 @@ public void drawInspector(ParentComponent root, double mouseX, double mouseY, bo public static class UtilityScreen extends Screen { - private static UtilityScreen INSTANCE; + private static SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(UtilityScreen::new); private Screen linkSourceScreen = null; - private UtilityScreen() { + private UtilityScreen(WindowContext ctx) { super(Text.empty()); + + ctx.framebufferResized().subscribe((newWidth, newHeight) -> { + this.init(MinecraftClient.getInstance(), ctx.scaledWidth(), ctx.scaledHeight()); + }); } public static UtilityScreen get() { - if (INSTANCE == null) { - INSTANCE = new UtilityScreen(); - - final var client = MinecraftClient.getInstance(); - INSTANCE.init( - client, - client.getWindow().getScaledWidth(), - client.getWindow().getScaledHeight() - ); - } - - return INSTANCE; + return CurrentWindowContext.current().get(KEY); } /** @@ -405,12 +401,5 @@ public void captureLinkSource() { public boolean handleTextClick(@Nullable Style style) { return super.handleTextClick(style); } - - static { - WindowResizeCallback.EVENT.register((client, window) -> { - if (INSTANCE == null) return; - INSTANCE.init(client, window.getScaledWidth(), window.getScaledHeight()); - }); - } } } diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index 892a2e69..3d9c558a 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -2,10 +2,12 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.ui.event.CharTyped; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.ui.window.context.WindowContext; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.EventStream; import io.wispforest.owo.util.OwoGlfwUtil; +import io.wispforest.owo.util.SupportsFeaturesImpl; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.GlDebug; @@ -20,7 +22,7 @@ import static org.lwjgl.glfw.GLFW.*; -public class FramebufferWindow implements AutoCloseable, WindowContext { +public class FramebufferWindow extends SupportsFeaturesImpl implements AutoCloseable, WindowContext { private int width; private int height; private final long handle; @@ -74,23 +76,35 @@ public FramebufferWindow(int width, int height, String name, long parentContext) this.disposeList = new ArrayList<>(); glfwSetWindowCloseCallback(handle, stowAndReturn(GLFWWindowCloseCallback.create(window -> { - windowClosedEvents.sink().onWindowClosed(); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + windowClosedEvents.sink().onWindowClosed(); + } }))); glfwSetFramebufferSizeCallback(handle, stowAndReturn(GLFWFramebufferSizeCallback.create(this::sizeChanged))); glfwSetCursorPosCallback(handle, stowAndReturn(GLFWCursorPosCallback.create((window, xpos, ypos) -> { - mouseMovedEvents.sink().onMouseMoved(xpos, ypos); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + mouseMovedEvents.sink().onMouseMoved(xpos, ypos); + } }))); glfwSetMouseButtonCallback(handle, stowAndReturn(GLFWMouseButtonCallback.create((window, button, action, mods) -> { - mouseButtonEvents.sink().onMouseButton(button, action == GLFW_RELEASE); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + mouseButtonEvents.sink().onMouseButton(button, action == GLFW_RELEASE); + } }))); glfwSetScrollCallback(handle, stowAndReturn(GLFWScrollCallback.create((window, xoffset, yoffset) -> { - mouseScrolledEvents.sink().onMouseScrolled(xoffset, yoffset); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + mouseScrolledEvents.sink().onMouseScrolled(xoffset, yoffset); + } }))); glfwSetKeyCallback(handle, stowAndReturn(GLFWKeyCallback.create((window, key, scancode, action, mods) -> { - keyPressedEvents.sink().onKeyPressed(key, scancode, mods, action == GLFW_RELEASE); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + keyPressedEvents.sink().onKeyPressed(key, scancode, mods, action == GLFW_RELEASE); + } }))); glfwSetCharModsCallback(handle, stowAndReturn(GLFWCharModsCallback.create((window, codepoint, mods) -> { - charTypedEvents.sink().onCharTyped((char) codepoint, mods); + try (var ignored = CurrentWindowContext.setCurrent(this)) { + charTypedEvents.sink().onCharTyped((char) codepoint, mods); + } }))); } @@ -221,6 +235,8 @@ public boolean closed() { @Override public void close() { + super.close(); + try (var ignored = OwoGlfwUtil.setContext(this.handle)) { GL32.glDeleteFramebuffers(this.localFramebuffer); } diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index f10a8826..1aaeba5f 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -36,42 +36,32 @@ public OwoWindow(int width, int height, String name, long parentContext) { windowClosed().subscribe(this::close); framebufferResized().subscribe((newWidth, newHeight) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - recalculateScale(); - adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); - } + recalculateScale(); + adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); }); mouseMoved().subscribe((x, y) -> { this.mouseX = (int) (x / scaleFactor); this.mouseY = (int) (y / scaleFactor); }); mouseButton().subscribe((button, released) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - if (released) { - adapter.mouseReleased(mouseX, mouseY, button); - } else { - adapter.mouseClicked(mouseX, mouseY, button); - } + if (released) { + adapter.mouseReleased(mouseX, mouseY, button); + } else { + adapter.mouseClicked(mouseX, mouseY, button); } }); mouseScrolled().subscribe((xOffset, yOffset) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - double amount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) - * client.options.getMouseWheelSensitivity().getValue(); - adapter.mouseScrolled(mouseX, mouseY, amount); - } + double amount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) + * client.options.getMouseWheelSensitivity().getValue(); + adapter.mouseScrolled(mouseX, mouseY, amount); }); keyPressed().subscribe((keyCode, scanCode, modifiers, released) -> { if (released) return; - try (var ignored = CurrentWindowContext.setCurrent(this)) { - adapter.keyPressed(keyCode, scanCode, modifiers); - } + adapter.keyPressed(keyCode, scanCode, modifiers); }); charTyped().subscribe((chr, modifiers) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - return adapter.charTyped(chr, modifiers); - } + return adapter.charTyped(chr, modifiers); }); } diff --git a/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java index ecff88e2..2bd753a6 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java @@ -1,7 +1,5 @@ package io.wispforest.owo.ui.window.context; -import net.minecraft.client.MinecraftClient; - public final class CurrentWindowContext { private static WindowContext CURRENT = VanillaWindowContext.MAIN; diff --git a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java index 7bfadbc8..f88578e0 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java @@ -4,10 +4,11 @@ import io.wispforest.owo.ui.window.WindowFramebufferResized; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.EventStream; +import io.wispforest.owo.util.SupportsFeaturesImpl; import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.Window; -public class VanillaWindowContext implements WindowContext { +public class VanillaWindowContext extends SupportsFeaturesImpl implements WindowContext { public static final VanillaWindowContext MAIN = new VanillaWindowContext(MinecraftClient.getInstance().getWindow()); private final Window window; diff --git a/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java index 3a7b6ac6..6d139f86 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java @@ -2,8 +2,9 @@ import io.wispforest.owo.ui.window.WindowFramebufferResized; import io.wispforest.owo.util.EventSource; +import io.wispforest.owo.util.SupportsFeatures; -public interface WindowContext { +public interface WindowContext extends SupportsFeatures { int framebufferWidth(); int framebufferHeight(); EventSource framebufferResized(); diff --git a/src/main/java/io/wispforest/owo/util/SupportsFeatures.java b/src/main/java/io/wispforest/owo/util/SupportsFeatures.java new file mode 100644 index 00000000..53f9c6c3 --- /dev/null +++ b/src/main/java/io/wispforest/owo/util/SupportsFeatures.java @@ -0,0 +1,9 @@ +package io.wispforest.owo.util; + +import java.util.function.Function; + +public interface SupportsFeatures> { + T get(Key key); + + record Key, T>(Function factory) { } +} diff --git a/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java b/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java new file mode 100644 index 00000000..8bd87ab2 --- /dev/null +++ b/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java @@ -0,0 +1,34 @@ +package io.wispforest.owo.util; + +import java.util.HashMap; +import java.util.Map; + +public abstract class SupportsFeaturesImpl> implements SupportsFeatures, AutoCloseable { + private final Map, Object> features = new HashMap<>(); + + @SuppressWarnings("unchecked") + @Override + public T get(Key key) { + T value = (T) features.get(key); + + if (value == null) { + value = key.factory().apply((S) this); + features.put(key, value); + } + + return value; + } + + @Override + public void close() { + for (var feature : features.values()) { + try { + if (feature instanceof AutoCloseable closeable) { + closeable.close(); + } + } catch (Exception e) { + throw new RuntimeException("Destroying " + this + "'s features failed", e); + } + } + } +} diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index 42b64e4e..44a90963 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -1,8 +1,11 @@ package io.wispforest.uwu.client; import io.wispforest.owo.ui.component.Components; +import io.wispforest.owo.ui.component.LabelComponent; import io.wispforest.owo.ui.container.Containers; import io.wispforest.owo.ui.container.FlowLayout; +import io.wispforest.owo.ui.container.RenderEffectWrapper; +import io.wispforest.owo.ui.core.Color; import io.wispforest.owo.ui.core.Insets; import io.wispforest.owo.ui.core.OwoUIAdapter; import io.wispforest.owo.ui.core.Sizing; @@ -43,6 +46,12 @@ protected void build(FlowLayout rootComponent) { } }); + inner.child(Containers.renderEffect(Components.label(Text.literal("breh!"))) + .>configure(component -> { + component.effect(RenderEffectWrapper.RenderEffect.rotate(45)); + component.effect(RenderEffectWrapper.RenderEffect.color(Color.BLUE)); + })); + inner .child(textbox .margins(Insets.vertical(5))) From 00cd637400695233d01fa4d94ceec54a08f0db38 Mon Sep 17 00:00:00 2001 From: Basique Date: Thu, 24 Aug 2023 02:04:19 +0300 Subject: [PATCH 10/20] changes? --- .../wispforest/owo/ui/util/GlDebugUtils.java | 41 +++++++++++++ .../io/wispforest/owo/ui/util/OwoGlUtil.java | 60 +++++++++++++++++++ .../owo/ui/window/FramebufferWindow.java | 19 +++--- .../wispforest/owo/ui/window/OwoWindow.java | 38 ++++++------ .../window/context/VanillaWindowContext.java | 5 ++ .../io/wispforest/owo/util/OwoGlfwUtil.java | 33 ---------- 6 files changed, 139 insertions(+), 57 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java create mode 100644 src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java delete mode 100644 src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java diff --git a/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java b/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java new file mode 100644 index 00000000..ab02b80c --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java @@ -0,0 +1,41 @@ +package io.wispforest.owo.ui.util; + + +import org.lwjgl.opengl.ARBDebugOutput; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.KHRDebug; + +public final class GlDebugUtils { + public static final boolean GL_KHR_debug = GL.getCapabilities().GL_KHR_debug; + + private GlDebugUtils() { + + } + + public static void labelObject(int type, int id, String name) { + if (GL_KHR_debug) { + KHRDebug.glObjectLabel(type, id, name); + } + } + + public static DebugGroup pushGroup(String name) { + if (GL_KHR_debug) { + KHRDebug.glPushDebugGroup(KHRDebug.GL_DEBUG_SOURCE_APPLICATION, 42, name); + } + + return new DebugGroup(); + } + + public static class DebugGroup implements AutoCloseable { + private DebugGroup() { + + } + + @Override + public void close() { + if (GL_KHR_debug) { + KHRDebug.glPopDebugGroup(); + } + } + } +} diff --git a/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java b/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java new file mode 100644 index 00000000..886f5b80 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java @@ -0,0 +1,60 @@ +package io.wispforest.owo.ui.util; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.systems.VertexSorter; +import org.joml.Matrix4f; +import org.lwjgl.glfw.GLFW; + +public final class OwoGlUtil { + private OwoGlUtil() { + + } + + public static ContextRestorer setContext(long handle) { + long old = GLFW.glfwGetCurrentContext(); + if (old == handle) return new ContextRestorer(-1); + + GLFW.glfwMakeContextCurrent(handle); + + return new ContextRestorer(old); + } + + public static ProjectionRestorer setProjectionMatrix(Matrix4f projectionMatrix, VertexSorter sorter) { + Matrix4f oldMatrix = RenderSystem.getProjectionMatrix(); + VertexSorter oldSorter = RenderSystem.getVertexSorting(); + + RenderSystem.setProjectionMatrix(projectionMatrix, sorter); + + return new ProjectionRestorer(oldMatrix, oldSorter); + } + + public static class ContextRestorer implements AutoCloseable { + private final long oldContext; + + private ContextRestorer(long old) { + this.oldContext = old; + } + + @Override + public void close() { + if (oldContext == -1) return; + + GLFW.glfwMakeContextCurrent(oldContext); + } + } + + public static class ProjectionRestorer implements AutoCloseable { + private final Matrix4f matrix; + private final VertexSorter sorter; + + private ProjectionRestorer(Matrix4f matrix, VertexSorter sorter) { + this.matrix = matrix; + this.sorter = sorter; + } + + @Override + public void close() { + RenderSystem.setProjectionMatrix(matrix, sorter); + } + } +} diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index 3d9c558a..b4a95bd3 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -2,11 +2,12 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.ui.event.CharTyped; +import io.wispforest.owo.ui.util.GlDebugUtils; import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.ui.window.context.WindowContext; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.EventStream; -import io.wispforest.owo.util.OwoGlfwUtil; +import io.wispforest.owo.ui.util.OwoGlUtil; import io.wispforest.owo.util.SupportsFeaturesImpl; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; @@ -48,7 +49,7 @@ public FramebufferWindow(int width, int height, String name, long parentContext) this.width = width; this.height = height; - try (var ignored = OwoGlfwUtil.setContext(0)) { + try (var ignored = OwoGlUtil.setContext(0)) { glfwDefaultWindowHints(); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); @@ -67,8 +68,9 @@ public FramebufferWindow(int width, int height, String name, long parentContext) } this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + GlDebugUtils.labelObject(GL32.GL_FRAMEBUFFER, this.framebuffer.fbo, "Main context framebuffer for " + this); - try (var ignored = OwoGlfwUtil.setContext(this.handle)) { + try (var ignored = OwoGlUtil.setContext(this.handle)) { GlDebug.enableDebug(client.options.glDebugVerbosity, true); } @@ -179,7 +181,8 @@ public EventSource charTyped() { public void present() { if (closed()) return; - try (var ignored = OwoGlfwUtil.setContext(handle)) { + try (var ignored = OwoGlUtil.setContext(handle); + var ignored1 = GlDebugUtils.pushGroup("Presenting framebuffer of " + this)) { // This code intentionally doesn't use Minecraft's RenderSystem // class, as it caches GL state that is invalid on this context. GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, localFramebuffer); @@ -197,13 +200,14 @@ public void present() { } private void initLocalFramebuffer() { - try (var ignored = OwoGlfwUtil.setContext(this.handle)) { + try (var ignored = OwoGlUtil.setContext(this.handle)) { if (localFramebuffer != 0) { GL32.glDeleteFramebuffers(localFramebuffer); } this.localFramebuffer = GL32.glGenFramebuffers(); GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); + GlDebugUtils.labelObject(GL32.GL_FRAMEBUFFER, this.localFramebuffer, "Local context framebuffer for " + this); GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); @@ -218,10 +222,11 @@ protected void sizeChanged(long handle, int width, int height) { this.width = width; this.height = height; - try (var ignored = OwoGlfwUtil.setContext(client.getWindow().getHandle())) { + try (var ignored = OwoGlUtil.setContext(client.getWindow().getHandle())) { framebuffer.delete(); this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + GlDebugUtils.labelObject(GL32.GL_FRAMEBUFFER, this.framebuffer.fbo, "Main context framebuffer for " + this); } initLocalFramebuffer(); @@ -237,7 +242,7 @@ public boolean closed() { public void close() { super.close(); - try (var ignored = OwoGlfwUtil.setContext(this.handle)) { + try (var ignored = OwoGlUtil.setContext(this.handle)) { GL32.glDeleteFramebuffers(this.localFramebuffer); } diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 1aaeba5f..9dd77c3b 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -4,6 +4,8 @@ import com.mojang.blaze3d.systems.VertexSorter; import io.wispforest.owo.ui.core.OwoUIAdapter; import io.wispforest.owo.ui.core.ParentComponent; +import io.wispforest.owo.ui.util.GlDebugUtils; +import io.wispforest.owo.ui.util.OwoGlUtil; import io.wispforest.owo.ui.window.context.CurrentWindowContext; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; @@ -97,7 +99,8 @@ public void recalculateScale() { public void render() { if (closed()) return; - try (var ignored = CurrentWindowContext.setCurrent(this)) { + try (var ignored = CurrentWindowContext.setCurrent(this); + var ignored1 = GlDebugUtils.pushGroup("Rendering " + this)) { framebuffer().beginWrite(true); RenderSystem.clearColor(0, 0, 0, 1); @@ -112,22 +115,23 @@ public void render() { 1000.0F, 21000.0F ); - RenderSystem.backupProjectionMatrix(); - RenderSystem.setProjectionMatrix(matrix4f, VertexSorter.BY_Z); - MatrixStack matrixStack = RenderSystem.getModelViewStack(); - matrixStack.push(); - matrixStack.loadIdentity(); - matrixStack.translate(0.0F, 0.0F, -11000.0F); - RenderSystem.applyModelViewMatrix(); - DiffuseLighting.enableGuiDepthLighting(); - - var consumers = client.getBufferBuilders().getEntityVertexConsumers(); - adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getTickDelta()); - consumers.draw(); - - RenderSystem.getModelViewStack().pop(); - RenderSystem.applyModelViewMatrix(); - RenderSystem.restoreProjectionMatrix(); + + try (var ignored2 = OwoGlUtil.setProjectionMatrix(matrix4f, VertexSorter.BY_Z)) { + MatrixStack matrixStack = RenderSystem.getModelViewStack(); + matrixStack.push(); + matrixStack.loadIdentity(); + matrixStack.translate(0.0F, 0.0F, -11000.0F); + RenderSystem.applyModelViewMatrix(); + DiffuseLighting.enableGuiDepthLighting(); + + var consumers = client.getBufferBuilders().getEntityVertexConsumers(); + adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getTickDelta()); + consumers.draw(); + + RenderSystem.getModelViewStack().pop(); + RenderSystem.applyModelViewMatrix(); + } + framebuffer().endWrite(); } diff --git a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java index f88578e0..ea9a25eb 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java @@ -59,4 +59,9 @@ public double scaleFactor() { public long handle() { return window.getHandle(); } + + @Override + public String toString() { + return "VanillaWindowContext[" + (this == MAIN ? "MAIN" : window) + "]"; + } } diff --git a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java b/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java deleted file mode 100644 index e2ac652f..00000000 --- a/src/main/java/io/wispforest/owo/util/OwoGlfwUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.wispforest.owo.util; - -import org.lwjgl.glfw.GLFW; - -public final class OwoGlfwUtil { - private OwoGlfwUtil() { - - } - - public static ContextRestorer setContext(long handle) { - long old = GLFW.glfwGetCurrentContext(); - if (old == handle) return new ContextRestorer(-1); - - GLFW.glfwMakeContextCurrent(handle); - - return new ContextRestorer(old); - } - - public static class ContextRestorer implements AutoCloseable { - private final long oldContext; - - private ContextRestorer(long old) { - this.oldContext = old; - } - - @Override - public void close() { - if (oldContext == -1) return; - - GLFW.glfwMakeContextCurrent(oldContext); - } - } -} From 5d9ed4f1c955d49d933c0435b45507a309bc1c31 Mon Sep 17 00:00:00 2001 From: Basique Date: Wed, 4 Oct 2023 23:39:16 +0300 Subject: [PATCH 11/20] fix stuff up --- src/main/java/io/wispforest/owo/ui/window/OwoWindow.java | 6 ++++-- .../java/io/wispforest/uwu/client/UwuTestWindow.java | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 9dd77c3b..edfead01 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -53,9 +53,11 @@ public OwoWindow(int width, int height, String name, long parentContext) { } }); mouseScrolled().subscribe((xOffset, yOffset) -> { - double amount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) + double yAmount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) * client.options.getMouseWheelSensitivity().getValue(); - adapter.mouseScrolled(mouseX, mouseY, amount); + double xAmount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(xOffset) : xOffset) + * client.options.getMouseWheelSensitivity().getValue(); + adapter.mouseScrolled(mouseX, mouseY, xAmount, yAmount); }); keyPressed().subscribe((keyCode, scanCode, modifiers, released) -> { if (released) return; diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index 44a90963..fe5c6270 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -52,6 +52,10 @@ protected void build(FlowLayout rootComponent) { component.effect(RenderEffectWrapper.RenderEffect.color(Color.BLUE)); })); + for (int i = 0; i < 100; i++) { + inner.child(Components.label(Text.of("breh!"))); + } + inner .child(textbox .margins(Insets.vertical(5))) From 2fd3dbb5e5355b3f051786044dafc4f910c321a8 Mon Sep 17 00:00:00 2001 From: Basique Date: Fri, 10 Nov 2023 23:40:52 +0300 Subject: [PATCH 12/20] when the closeable is infallible! --- .../wispforest/owo/ui/util/GlDebugUtils.java | 26 +++--------- .../io/wispforest/owo/ui/util/OwoGlUtil.java | 41 +++---------------- .../wispforest/owo/ui/window/OpenWindows.java | 18 ++------ .../wispforest/owo/ui/window/OwoWindow.java | 3 +- .../window/context/CurrentWindowContext.java | 21 +++------- .../owo/util/InfallibleCloseable.java | 10 +++++ 6 files changed, 33 insertions(+), 86 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/util/InfallibleCloseable.java diff --git a/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java b/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java index ab02b80c..a8504606 100644 --- a/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java +++ b/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java @@ -1,11 +1,11 @@ package io.wispforest.owo.ui.util; - -import org.lwjgl.opengl.ARBDebugOutput; +import io.wispforest.owo.util.InfallibleCloseable; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.KHRDebug; public final class GlDebugUtils { + public static final boolean DEBUG_GROUPS_ENABLED = Boolean.getBoolean("owo.glDebugGroups"); public static final boolean GL_KHR_debug = GL.getCapabilities().GL_KHR_debug; private GlDebugUtils() { @@ -18,24 +18,10 @@ public static void labelObject(int type, int id, String name) { } } - public static DebugGroup pushGroup(String name) { - if (GL_KHR_debug) { - KHRDebug.glPushDebugGroup(KHRDebug.GL_DEBUG_SOURCE_APPLICATION, 42, name); - } - - return new DebugGroup(); - } + public static InfallibleCloseable pushGroup(String name) { + if (!GL_KHR_debug || !DEBUG_GROUPS_ENABLED) return InfallibleCloseable.empty(); - public static class DebugGroup implements AutoCloseable { - private DebugGroup() { - - } - - @Override - public void close() { - if (GL_KHR_debug) { - KHRDebug.glPopDebugGroup(); - } - } + KHRDebug.glPushDebugGroup(KHRDebug.GL_DEBUG_SOURCE_APPLICATION, 42, name); + return KHRDebug::glPopDebugGroup; } } diff --git a/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java b/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java index 886f5b80..388534c7 100644 --- a/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java +++ b/src/main/java/io/wispforest/owo/ui/util/OwoGlUtil.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.VertexSorter; +import io.wispforest.owo.util.InfallibleCloseable; import org.joml.Matrix4f; import org.lwjgl.glfw.GLFW; @@ -10,51 +11,21 @@ private OwoGlUtil() { } - public static ContextRestorer setContext(long handle) { + public static InfallibleCloseable setContext(long handle) { long old = GLFW.glfwGetCurrentContext(); - if (old == handle) return new ContextRestorer(-1); + if (old == handle) return InfallibleCloseable.empty(); GLFW.glfwMakeContextCurrent(handle); - return new ContextRestorer(old); + return () -> GLFW.glfwMakeContextCurrent(old); } - public static ProjectionRestorer setProjectionMatrix(Matrix4f projectionMatrix, VertexSorter sorter) { + public static InfallibleCloseable setProjectionMatrix(Matrix4f projectionMatrix, VertexSorter sorter) { Matrix4f oldMatrix = RenderSystem.getProjectionMatrix(); VertexSorter oldSorter = RenderSystem.getVertexSorting(); RenderSystem.setProjectionMatrix(projectionMatrix, sorter); - return new ProjectionRestorer(oldMatrix, oldSorter); - } - - public static class ContextRestorer implements AutoCloseable { - private final long oldContext; - - private ContextRestorer(long old) { - this.oldContext = old; - } - - @Override - public void close() { - if (oldContext == -1) return; - - GLFW.glfwMakeContextCurrent(oldContext); - } - } - - public static class ProjectionRestorer implements AutoCloseable { - private final Matrix4f matrix; - private final VertexSorter sorter; - - private ProjectionRestorer(Matrix4f matrix, VertexSorter sorter) { - this.matrix = matrix; - this.sorter = sorter; - } - - @Override - public void close() { - RenderSystem.setProjectionMatrix(matrix, sorter); - } + return () -> RenderSystem.setProjectionMatrix(oldMatrix, oldSorter); } } diff --git a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java index ab2b0728..8390b6c4 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java +++ b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java @@ -1,5 +1,6 @@ package io.wispforest.owo.ui.window; +import io.wispforest.owo.util.InfallibleCloseable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.UnmodifiableView; @@ -15,10 +16,10 @@ private OpenWindows() { } - static WindowRegistration add(OwoWindow window) { + static InfallibleCloseable add(OwoWindow window) { WINDOWS.add(window); - return new WindowRegistration(window); + return () -> WINDOWS.remove(window); } public static @UnmodifiableView List> windows() { @@ -31,17 +32,4 @@ public static void renderAll() { window.render(); } } - - public static class WindowRegistration implements AutoCloseable { - private final OwoWindow window; - - private WindowRegistration(OwoWindow window) { - this.window = window; - } - - @Override - public void close() { - WINDOWS.remove(window); - } - } } diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index edfead01..49a30916 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -7,6 +7,7 @@ import io.wispforest.owo.ui.util.GlDebugUtils; import io.wispforest.owo.ui.util.OwoGlUtil; import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.util.InfallibleCloseable; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.render.DiffuseLighting; @@ -19,7 +20,7 @@ public abstract class OwoWindow extends FramebufferWi private int scaledWidth; private int scaledHeight; private final OwoUIAdapter adapter; - private final OpenWindows.WindowRegistration registration; + private final InfallibleCloseable registration; private int mouseX = -1; private int mouseY = -1; diff --git a/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java index 2bd753a6..06a56ea6 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/CurrentWindowContext.java @@ -1,5 +1,7 @@ package io.wispforest.owo.ui.window.context; +import io.wispforest.owo.util.InfallibleCloseable; + public final class CurrentWindowContext { private static WindowContext CURRENT = VanillaWindowContext.MAIN; @@ -7,26 +9,15 @@ private CurrentWindowContext() { } - public static WindowResetter setCurrent(WindowContext window) { + public static InfallibleCloseable setCurrent(WindowContext window) { var old = CURRENT; + CURRENT = window; - return new WindowResetter(old); + + return () -> CURRENT = old; } public static WindowContext current() { return CURRENT; } - - public static class WindowResetter implements AutoCloseable { - private final WindowContext window; - - private WindowResetter(WindowContext window) { - this.window = window; - } - - @Override - public void close() { - CurrentWindowContext.CURRENT = window; - } - } } diff --git a/src/main/java/io/wispforest/owo/util/InfallibleCloseable.java b/src/main/java/io/wispforest/owo/util/InfallibleCloseable.java new file mode 100644 index 00000000..ee2c88a2 --- /dev/null +++ b/src/main/java/io/wispforest/owo/util/InfallibleCloseable.java @@ -0,0 +1,10 @@ +package io.wispforest.owo.util; + +public interface InfallibleCloseable extends AutoCloseable { + static InfallibleCloseable empty() { + return () -> {}; + } + + @Override + void close(); +} From 957512bfa10b53fa292728aeae41370988af6480 Mon Sep 17 00:00:00 2001 From: Basique Date: Tue, 21 Nov 2023 23:39:03 +0300 Subject: [PATCH 13/20] mmm icons --- .../owo/ui/window/FramebufferWindow.java | 67 +++++++++++++++++++ .../wispforest/owo/ui/window/OwoWindow.java | 4 +- .../wispforest/uwu/client/UwuTestWindow.java | 13 ++-- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index b4a95bd3..4f95c55f 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -14,10 +14,19 @@ import net.minecraft.client.gl.GlDebug; import net.minecraft.client.gl.SimpleFramebuffer; import net.minecraft.client.render.Tessellator; +import net.minecraft.client.texture.NativeImage; +import net.minecraft.resource.InputSupplier; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; import org.lwjgl.glfw.*; import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.NativeResource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -26,9 +35,12 @@ public class FramebufferWindow extends SupportsFeaturesImpl implements AutoCloseable, WindowContext { private int width; private int height; + private final long handle; + private Framebuffer framebuffer; private int localFramebuffer = 0; + protected final MinecraftClient client = MinecraftClient.getInstance(); private final List disposeList; @@ -234,6 +246,61 @@ protected void sizeChanged(long handle, int width, int height) { framebufferResizedEvents.sink().onFramebufferResized(width, height); } + public static boolean supportsIcons() { + int platform = GLFW.glfwGetPlatform(); + return platform == GLFW_PLATFORM_WIN32 || platform == GLFW_PLATFORM_X11; + } + + public void setIconTextures(ResourceManager manager, List iconIds) { + if (!supportsIcons()) return; + + List iconImages = new ArrayList<>(iconIds.size()); + + try { + for (Identifier iconId : iconIds) { + var icon = manager.getResource(iconId).orElse(null); + + if (icon == null) continue; + + try { + iconImages.add(NativeImage.read(icon.getInputStream())); + } catch (IOException e) { + throw new RuntimeException("Couldn't open icon " + iconId, e); + } + } + + setIcon(iconImages); + } finally { + iconImages.forEach(NativeImage::close); + } + } + + public void setIcon(List icons) { + if (!supportsIcons()) return; + + List freeList = new ArrayList<>(icons.size()); + try (MemoryStack memoryStack = MemoryStack.stackPush()) { + GLFWImage.Buffer buffer = GLFWImage.malloc(icons.size(), memoryStack); + + for (int i = 0; i < icons.size(); i++) { + NativeImage icon = icons.get(i); + ByteBuffer imgBuffer = MemoryUtil.memAlloc(icon.getWidth() * icon.getHeight() * 4); + freeList.add(imgBuffer); + imgBuffer.asIntBuffer().put(icon.copyPixelsRgba()); + + buffer + .position(i) + .width(icon.getWidth()) + .height(icon.getHeight()) + .pixels(imgBuffer); + } + + GLFW.glfwSetWindowIcon(this.handle, buffer.position(0)); + } finally { + freeList.forEach(MemoryUtil::memFree); + } + } + public boolean closed() { return this.disposeList.isEmpty(); } diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 49a30916..68836246 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -65,9 +65,7 @@ public OwoWindow(int width, int height, String name, long parentContext) { adapter.keyPressed(keyCode, scanCode, modifiers); }); - charTyped().subscribe((chr, modifiers) -> { - return adapter.charTyped(chr, modifiers); - }); + charTyped().subscribe(adapter::charTyped); } protected abstract OwoUIAdapter createAdapter(); diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index fe5c6270..e52084ce 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -13,10 +13,15 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; + +import java.util.List; public class UwuTestWindow extends OwoWindow { public UwuTestWindow() { super(640, 480, "uωu test window!", MinecraftClient.getInstance().getWindow().getHandle()); + + setIconTextures(MinecraftClient.getInstance().getResourceManager(), List.of(new Identifier("owo", "icon.png"))); } @Override @@ -29,7 +34,7 @@ protected void build(FlowLayout rootComponent) { rootComponent.padding(Insets.of(10)); var inner = Containers.verticalFlow(Sizing.content(), Sizing.content()); - rootComponent.child(Containers.verticalScroll(Sizing.content(), Sizing.fill(100), inner)); + rootComponent.child(Containers.verticalScroll(Sizing.fill(100), Sizing.fill(100), inner)); inner.child(Components.label(Text.of("Are you an owl?"))); @@ -46,12 +51,6 @@ protected void build(FlowLayout rootComponent) { } }); - inner.child(Containers.renderEffect(Components.label(Text.literal("breh!"))) - .>configure(component -> { - component.effect(RenderEffectWrapper.RenderEffect.rotate(45)); - component.effect(RenderEffectWrapper.RenderEffect.color(Color.BLUE)); - })); - for (int i = 0; i < 100; i++) { inner.child(Components.label(Text.of("breh!"))); } From 3d4d55d5fa8a909322362d8ff8ab8be95366a1a3 Mon Sep 17 00:00:00 2001 From: Basique Date: Sun, 30 Jun 2024 22:30:08 +0300 Subject: [PATCH 14/20] remove GlDebugUtils it's not really relevant and should probably be in a separate PR --- .../wispforest/owo/ui/util/GlDebugUtils.java | 27 ------------------- .../owo/ui/window/FramebufferWindow.java | 9 +------ .../wispforest/owo/ui/window/OwoWindow.java | 5 +--- 3 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java diff --git a/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java b/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java deleted file mode 100644 index a8504606..00000000 --- a/src/main/java/io/wispforest/owo/ui/util/GlDebugUtils.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.wispforest.owo.ui.util; - -import io.wispforest.owo.util.InfallibleCloseable; -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.KHRDebug; - -public final class GlDebugUtils { - public static final boolean DEBUG_GROUPS_ENABLED = Boolean.getBoolean("owo.glDebugGroups"); - public static final boolean GL_KHR_debug = GL.getCapabilities().GL_KHR_debug; - - private GlDebugUtils() { - - } - - public static void labelObject(int type, int id, String name) { - if (GL_KHR_debug) { - KHRDebug.glObjectLabel(type, id, name); - } - } - - public static InfallibleCloseable pushGroup(String name) { - if (!GL_KHR_debug || !DEBUG_GROUPS_ENABLED) return InfallibleCloseable.empty(); - - KHRDebug.glPushDebugGroup(KHRDebug.GL_DEBUG_SOURCE_APPLICATION, 42, name); - return KHRDebug::glPopDebugGroup; - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java index 196d7043..7f1fb0fa 100644 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java @@ -2,7 +2,6 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.ui.event.CharTyped; -import io.wispforest.owo.ui.util.GlDebugUtils; import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.ui.window.context.WindowContext; import io.wispforest.owo.util.EventSource; @@ -15,7 +14,6 @@ import net.minecraft.client.gl.SimpleFramebuffer; import net.minecraft.client.render.Tessellator; import net.minecraft.client.texture.NativeImage; -import net.minecraft.resource.InputSupplier; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; import org.lwjgl.glfw.*; @@ -25,7 +23,6 @@ import org.lwjgl.system.NativeResource; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -80,7 +77,6 @@ public FramebufferWindow(int width, int height, String name, long parentContext) } this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); - GlDebugUtils.labelObject(GL32.GL_FRAMEBUFFER, this.framebuffer.fbo, "Main context framebuffer for " + this); try (var ignored = OwoGlUtil.setContext(this.handle)) { GlDebug.enableDebug(client.options.glDebugVerbosity, true); @@ -193,8 +189,7 @@ public EventSource charTyped() { public void present() { if (closed()) return; - try (var ignored = OwoGlUtil.setContext(handle); - var ignored1 = GlDebugUtils.pushGroup("Presenting framebuffer of " + this)) { + try (var ignored = OwoGlUtil.setContext(handle)) { // This code intentionally doesn't use Minecraft's RenderSystem // class, as it caches GL state that is invalid on this context. GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, localFramebuffer); @@ -219,7 +214,6 @@ private void initLocalFramebuffer() { this.localFramebuffer = GL32.glGenFramebuffers(); GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); - GlDebugUtils.labelObject(GL32.GL_FRAMEBUFFER, this.localFramebuffer, "Local context framebuffer for " + this); GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); @@ -238,7 +232,6 @@ protected void sizeChanged(long handle, int width, int height) { framebuffer.delete(); this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); - GlDebugUtils.labelObject(GL32.GL_FRAMEBUFFER, this.framebuffer.fbo, "Main context framebuffer for " + this); } initLocalFramebuffer(); diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 3741889c..279310dd 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -4,14 +4,12 @@ import com.mojang.blaze3d.systems.VertexSorter; import io.wispforest.owo.ui.core.OwoUIAdapter; import io.wispforest.owo.ui.core.ParentComponent; -import io.wispforest.owo.ui.util.GlDebugUtils; import io.wispforest.owo.ui.util.OwoGlUtil; import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.util.InfallibleCloseable; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.render.DiffuseLighting; -import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; import org.joml.Matrix4fStack; import org.lwjgl.opengl.GL32; @@ -101,8 +99,7 @@ public void recalculateScale() { public void render() { if (closed()) return; - try (var ignored = CurrentWindowContext.setCurrent(this); - var ignored1 = GlDebugUtils.pushGroup("Rendering " + this)) { + try (var ignored = CurrentWindowContext.setCurrent(this)) { framebuffer().beginWrite(true); RenderSystem.clearColor(0, 0, 0, 1); From 5f18440f5c8d37ae8c3c815199b3305b565f65d1 Mon Sep 17 00:00:00 2001 From: Basique Date: Mon, 1 Jul 2024 00:04:20 +0300 Subject: [PATCH 15/20] fix some stuff --- .../io/wispforest/owo/ui/container/RenderEffectWrapper.java | 2 +- src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java b/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java index 15b6372a..09c0a7f1 100644 --- a/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java +++ b/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java @@ -123,7 +123,7 @@ public void clearEffects() { } private static final class FramebuffersFeature implements AutoCloseable { - public static SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(FramebuffersFeature::new); + private static final SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(FramebuffersFeature::new); private final WindowContext ctx; private final List framebuffers; diff --git a/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java b/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java index 38011861..c9e8bf29 100644 --- a/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java +++ b/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java @@ -351,13 +351,15 @@ public void drawInspector(ParentComponent root, double mouseX, double mouseY, bo public static class UtilityScreen extends Screen { - private static SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(UtilityScreen::new); + private static final SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(UtilityScreen::new); private Screen linkSourceScreen = null; private UtilityScreen(WindowContext ctx) { super(Text.empty()); + this.init(MinecraftClient.getInstance(), ctx.scaledWidth(), ctx.scaledHeight()); + ctx.framebufferResized().subscribe((newWidth, newHeight) -> { this.init(MinecraftClient.getInstance(), ctx.scaledWidth(), ctx.scaledHeight()); }); From 743f1334e3d6361230e26a46570ff8c989e4ad19 Mon Sep 17 00:00:00 2001 From: Basique Date: Wed, 3 Jul 2024 23:28:45 +0300 Subject: [PATCH 16/20] add support for mouse drag --- .../wispforest/owo/ui/window/OwoWindow.java | 30 +++++++++++++++++-- .../wispforest/uwu/client/UwuTestWindow.java | 26 +++++++++------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 279310dd..0c0f7167 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -23,6 +23,9 @@ public abstract class OwoWindow extends FramebufferWi private int mouseX = -1; private int mouseY = -1; + private int deltaX = 0; + private int deltaY = 0; + private int activeButton = -1; public OwoWindow(int width, int height, String name, long parentContext) { super(width, height, name, parentContext); @@ -42,13 +45,23 @@ public OwoWindow(int width, int height, String name, long parentContext) { adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); }); mouseMoved().subscribe((x, y) -> { - this.mouseX = (int) (x / scaleFactor); - this.mouseY = (int) (y / scaleFactor); + int newX = (int) (x / scaleFactor); + int newY = (int) (y / scaleFactor); + + deltaX += newX - mouseX; + deltaY += newY - mouseY; + + mouseY = newY; + mouseX = newX; }); mouseButton().subscribe((button, released) -> { if (released) { + this.activeButton = -1; + adapter.mouseReleased(mouseX, mouseY, button); } else { + this.activeButton = button; + adapter.mouseClicked(mouseX, mouseY, button); } }); @@ -96,10 +109,23 @@ public void recalculateScale() { this.scaledHeight = (int) Math.ceil((double) this.framebufferHeight() / scaleFactor); } + private void tickMouse() { + if (deltaX == 0 && this.deltaY == 0) return; + + adapter.mouseMoved(mouseX, mouseY); + + if (activeButton != -1) adapter.mouseDragged(mouseX, mouseY, activeButton, deltaX, deltaY); + + deltaX = 0; + deltaY = 0; + } + public void render() { if (closed()) return; try (var ignored = CurrentWindowContext.setCurrent(this)) { + tickMouse(); + framebuffer().beginWrite(true); RenderSystem.clearColor(0, 0, 0, 1); diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index cbf69ef6..0d715614 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -1,14 +1,10 @@ package io.wispforest.uwu.client; import io.wispforest.owo.ui.component.Components; -import io.wispforest.owo.ui.component.LabelComponent; +import io.wispforest.owo.ui.component.SlimSliderComponent; import io.wispforest.owo.ui.container.Containers; import io.wispforest.owo.ui.container.FlowLayout; -import io.wispforest.owo.ui.container.RenderEffectWrapper; -import io.wispforest.owo.ui.core.Color; -import io.wispforest.owo.ui.core.Insets; -import io.wispforest.owo.ui.core.OwoUIAdapter; -import io.wispforest.owo.ui.core.Sizing; +import io.wispforest.owo.ui.core.*; import io.wispforest.owo.ui.window.OwoWindow; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; @@ -31,6 +27,7 @@ protected OwoUIAdapter createAdapter() { @Override protected void build(FlowLayout rootComponent) { + rootComponent.surface(Surface.DARK_PANEL); rootComponent.padding(Insets.of(10)); var inner = Containers.verticalFlow(Sizing.content(), Sizing.content()); @@ -51,13 +48,22 @@ protected void build(FlowLayout rootComponent) { } }); - for (int i = 0; i < 100; i++) { - inner.child(Components.label(Text.of("breh!"))); - } - inner .child(textbox .margins(Insets.vertical(5))) .child(statusLabel); + + inner.child(Containers.horizontalFlow(Sizing.content(), Sizing.content()) + .child(Components.label(Text.literal("owl level: "))) + .child(Components.slider(Sizing.fixed(200)))); + + inner.child(Containers.horizontalFlow(Sizing.content(), Sizing.content()) + .child(Components.label(Text.literal("owl level (slim): "))) + .child(Components.slimSlider(SlimSliderComponent.Axis.HORIZONTAL) + .horizontalSizing(Sizing.fixed(200)))); + + for (int i = 0; i < 100; i++) { + inner.child(Components.label(Text.of("breh!"))); + } } } From a9dd930c5e34db8d49188c3bbc0708b94698be2d Mon Sep 17 00:00:00 2001 From: Basique Date: Thu, 4 Jul 2024 00:35:55 +0300 Subject: [PATCH 17/20] fold FramebufferWindow into OwoWindow also turn OwoWindow into something more builder-style --- .../owo/ui/window/FramebufferWindow.java | 315 ---------------- .../wispforest/owo/ui/window/OpenWindows.java | 6 +- .../wispforest/owo/ui/window/OwoWindow.java | 350 +++++++++++++++--- .../owo/ui/window/WindowClosed.java | 15 - .../wispforest/owo/ui/window/WindowIcon.java | 61 +++ .../owo/ui/window/WindowKeyPressed.java | 15 - .../owo/ui/window/WindowMouseButton.java | 15 - .../owo/ui/window/WindowMouseMoved.java | 15 - .../owo/ui/window/WindowMouseScrolled.java | 15 - .../window/context/VanillaWindowContext.java | 12 +- .../owo/ui/window/context/WindowContext.java | 2 + .../owo/util/SupportsFeaturesImpl.java | 5 +- .../uwu/client/ComponentTestScreen.java | 1 - .../uwu/client/SelectUwuScreenScreen.java | 1 + .../wispforest/uwu/client/UwuTestWindow.java | 24 +- 15 files changed, 396 insertions(+), 456 deletions(-) delete mode 100644 src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java delete mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowClosed.java create mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowIcon.java delete mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java delete mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java delete mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java delete mode 100644 src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java diff --git a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java b/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java deleted file mode 100644 index 7f1fb0fa..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/FramebufferWindow.java +++ /dev/null @@ -1,315 +0,0 @@ -package io.wispforest.owo.ui.window; - -import com.mojang.blaze3d.systems.RenderSystem; -import io.wispforest.owo.ui.event.CharTyped; -import io.wispforest.owo.ui.window.context.CurrentWindowContext; -import io.wispforest.owo.ui.window.context.WindowContext; -import io.wispforest.owo.util.EventSource; -import io.wispforest.owo.util.EventStream; -import io.wispforest.owo.ui.util.OwoGlUtil; -import io.wispforest.owo.util.SupportsFeaturesImpl; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.Framebuffer; -import net.minecraft.client.gl.GlDebug; -import net.minecraft.client.gl.SimpleFramebuffer; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.texture.NativeImage; -import net.minecraft.resource.ResourceManager; -import net.minecraft.util.Identifier; -import org.lwjgl.glfw.*; -import org.lwjgl.opengl.GL32; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.system.MemoryUtil; -import org.lwjgl.system.NativeResource; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import static org.lwjgl.glfw.GLFW.*; - -public class FramebufferWindow extends SupportsFeaturesImpl implements AutoCloseable, WindowContext { - private int width; - private int height; - - private final long handle; - - private Framebuffer framebuffer; - private int localFramebuffer = 0; - - protected final MinecraftClient client = MinecraftClient.getInstance(); - - private final List disposeList; - - private final EventStream windowClosedEvents = WindowClosed.newStream(); - private final EventStream framebufferResizedEvents = WindowFramebufferResized.newStream(); - private final EventStream mouseMovedEvents = WindowMouseMoved.newStream(); - private final EventStream mouseButtonEvents = WindowMouseButton.newStream(); - private final EventStream mouseScrolledEvents = WindowMouseScrolled.newStream(); - private final EventStream keyPressedEvents = WindowKeyPressed.newStream(); - private final EventStream charTypedEvents = CharTyped.newStream(); - - public FramebufferWindow(int width, int height, String name, long parentContext) { - if (glfwGetCurrentContext() != MinecraftClient.getInstance().getWindow().getHandle()) { - throw new IllegalStateException("Window was created on alternate GL context"); - } - - this.width = width; - this.height = height; - - try (var ignored = OwoGlUtil.setContext(0)) { - glfwDefaultWindowHints(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1); - handle = glfwCreateWindow(width, height, name, 0, parentContext); - - if (handle == 0) { - throw new IllegalStateException("OwoWindow creation failed due to GLFW error"); - } - - glfwMakeContextCurrent(handle); - glfwSwapInterval(0); - } - - this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); - - try (var ignored = OwoGlUtil.setContext(this.handle)) { - GlDebug.enableDebug(client.options.glDebugVerbosity, true); - } - - initLocalFramebuffer(); - - this.disposeList = new ArrayList<>(); - glfwSetWindowCloseCallback(handle, stowAndReturn(GLFWWindowCloseCallback.create(window -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - windowClosedEvents.sink().onWindowClosed(); - } - }))); - glfwSetFramebufferSizeCallback(handle, stowAndReturn(GLFWFramebufferSizeCallback.create(this::sizeChanged))); - glfwSetCursorPosCallback(handle, stowAndReturn(GLFWCursorPosCallback.create((window, xpos, ypos) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - mouseMovedEvents.sink().onMouseMoved(xpos, ypos); - } - }))); - glfwSetMouseButtonCallback(handle, stowAndReturn(GLFWMouseButtonCallback.create((window, button, action, mods) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - mouseButtonEvents.sink().onMouseButton(button, action == GLFW_RELEASE); - } - }))); - glfwSetScrollCallback(handle, stowAndReturn(GLFWScrollCallback.create((window, xoffset, yoffset) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - mouseScrolledEvents.sink().onMouseScrolled(xoffset, yoffset); - } - }))); - glfwSetKeyCallback(handle, stowAndReturn(GLFWKeyCallback.create((window, key, scancode, action, mods) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - keyPressedEvents.sink().onKeyPressed(key, scancode, mods, action == GLFW_RELEASE); - } - }))); - glfwSetCharModsCallback(handle, stowAndReturn(GLFWCharModsCallback.create((window, codepoint, mods) -> { - try (var ignored = CurrentWindowContext.setCurrent(this)) { - charTypedEvents.sink().onCharTyped((char) codepoint, mods); - } - }))); - } - - private T stowAndReturn(T resource) { - this.disposeList.add(resource); - return resource; - } - - public Framebuffer framebuffer() { - return framebuffer; - } - - @Override - public int framebufferWidth() { - return width; - } - - @Override - public int framebufferHeight() { - return height; - } - - @Override - public int scaledWidth() { - return framebufferWidth(); - } - - @Override - public int scaledHeight() { - return framebufferHeight(); - } - - @Override - public double scaleFactor() { - return 1; - } - - @Override - public long handle() { - return handle; - } - - public EventSource windowClosed() { - return windowClosedEvents.source(); - } - - @Override - public EventSource framebufferResized() { - return framebufferResizedEvents.source(); - } - - public EventSource mouseMoved() { - return mouseMovedEvents.source(); - } - - public EventSource mouseButton() { - return mouseButtonEvents.source(); - } - - public EventSource mouseScrolled() { - return mouseScrolledEvents.source(); - } - - public EventSource keyPressed() { - return keyPressedEvents.source(); - } - - public EventSource charTyped() { - return charTypedEvents.source(); - } - - public void present() { - if (closed()) return; - - try (var ignored = OwoGlUtil.setContext(handle)) { - // This code intentionally doesn't use Minecraft's RenderSystem - // class, as it caches GL state that is invalid on this context. - GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, localFramebuffer); - GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, 0); - - GL32.glClearColor(1, 1, 1, 1); - GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); - GL32.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL32.GL_COLOR_BUFFER_BIT, GL32.GL_NEAREST); - - // Intentionally doesn't poll events so that all events are on the main window - RenderSystem.replayQueue(); - Tessellator.getInstance().clear(); - GLFW.glfwSwapBuffers(this.handle); - } - } - - private void initLocalFramebuffer() { - try (var ignored = OwoGlUtil.setContext(this.handle)) { - if (localFramebuffer != 0) { - GL32.glDeleteFramebuffers(localFramebuffer); - } - - this.localFramebuffer = GL32.glGenFramebuffers(); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); - GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); - - int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); - if (status != GL32.GL_FRAMEBUFFER_COMPLETE) - throw new IllegalStateException("Failed to create local framebuffer!"); - } - } - - protected void sizeChanged(long handle, int width, int height) { - if (framebuffer.viewportWidth == width && framebuffer.viewportHeight == height) return; - - this.width = width; - this.height = height; - - try (var ignored = OwoGlUtil.setContext(client.getWindow().getHandle())) { - framebuffer.delete(); - - this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); - } - - initLocalFramebuffer(); - - framebufferResizedEvents.sink().onFramebufferResized(width, height); - } - - public static boolean supportsIcons() { - int platform = GLFW.glfwGetPlatform(); - return platform == GLFW_PLATFORM_WIN32 || platform == GLFW_PLATFORM_X11; - } - - public void setIconTextures(ResourceManager manager, List iconIds) { - if (!supportsIcons()) return; - - List iconImages = new ArrayList<>(iconIds.size()); - - try { - for (Identifier iconId : iconIds) { - var icon = manager.getResource(iconId).orElse(null); - - if (icon == null) continue; - - try { - iconImages.add(NativeImage.read(icon.getInputStream())); - } catch (IOException e) { - throw new RuntimeException("Couldn't open icon " + iconId, e); - } - } - - setIcon(iconImages); - } finally { - iconImages.forEach(NativeImage::close); - } - } - - public void setIcon(List icons) { - if (!supportsIcons()) return; - - List freeList = new ArrayList<>(icons.size()); - try (MemoryStack memoryStack = MemoryStack.stackPush()) { - GLFWImage.Buffer buffer = GLFWImage.malloc(icons.size(), memoryStack); - - for (int i = 0; i < icons.size(); i++) { - NativeImage icon = icons.get(i); - ByteBuffer imgBuffer = MemoryUtil.memAlloc(icon.getWidth() * icon.getHeight() * 4); - freeList.add(imgBuffer); - imgBuffer.asIntBuffer().put(icon.copyPixelsRgba()); - - buffer - .position(i) - .width(icon.getWidth()) - .height(icon.getHeight()) - .pixels(imgBuffer); - } - - GLFW.glfwSetWindowIcon(this.handle, buffer.position(0)); - } finally { - freeList.forEach(MemoryUtil::memFree); - } - } - - public boolean closed() { - return this.disposeList.isEmpty(); - } - - @Override - public void close() { - super.close(); - - try (var ignored = OwoGlUtil.setContext(this.handle)) { - GL32.glDeleteFramebuffers(this.localFramebuffer); - } - - this.framebuffer.delete(); - glfwDestroyWindow(this.handle); - - this.disposeList.forEach(NativeResource::free); - this.disposeList.clear(); - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java index 8390b6c4..c37cadec 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java +++ b/src/main/java/io/wispforest/owo/ui/window/OpenWindows.java @@ -16,10 +16,12 @@ private OpenWindows() { } - static InfallibleCloseable add(OwoWindow window) { + static void add(OwoWindow window) { WINDOWS.add(window); + } - return () -> WINDOWS.remove(window); + static void remove(OwoWindow window) { + WINDOWS.remove(window); } public static @UnmodifiableView List> windows() { diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 0c0f7167..5b6ef74a 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -6,20 +6,53 @@ import io.wispforest.owo.ui.core.ParentComponent; import io.wispforest.owo.ui.util.OwoGlUtil; import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.WindowContext; +import io.wispforest.owo.util.EventSource; +import io.wispforest.owo.util.EventStream; import io.wispforest.owo.util.InfallibleCloseable; +import io.wispforest.owo.util.SupportsFeaturesImpl; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.GlDebug; +import net.minecraft.client.gl.SimpleFramebuffer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.render.DiffuseLighting; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.texture.NativeImage; import org.joml.Matrix4f; import org.joml.Matrix4fStack; +import org.lwjgl.glfw.*; import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.NativeResource; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFW.glfwSetCharModsCallback; + +public abstract class OwoWindow extends SupportsFeaturesImpl implements WindowContext { + private String title = "owo-ui window"; + private int screenWidth = 854; + private int screenHeight = 480; + private WindowIcon icon; + + private int framebufferWidth; + private int framebufferHeight; + + private long handle = 0; + private Framebuffer framebuffer; + private int localFramebuffer = 0; + private final List disposeList = new ArrayList<>(); + private final MinecraftClient client = MinecraftClient.getInstance(); -public abstract class OwoWindow extends FramebufferWindow { private int scaleFactor; private int scaledWidth; private int scaledHeight; - private final OwoUIAdapter adapter; - private final InfallibleCloseable registration; + protected OwoUIAdapter uiAdapter = null; private int mouseX = -1; private int mouseY = -1; @@ -27,57 +60,224 @@ public abstract class OwoWindow extends FramebufferWi private int deltaY = 0; private int activeButton = -1; - public OwoWindow(int width, int height, String name, long parentContext) { - super(width, height, name, parentContext); - recalculateScale(); + private final EventStream framebufferResizedEvents = WindowFramebufferResized.newStream(); - try (var ignored = CurrentWindowContext.setCurrent(this)) { - this.adapter = createAdapter(); - build(this.adapter.rootComponent); - this.adapter.inflateAndMount(); + public OwoWindow() { + + } + + public OwoWindow size(int screenWidth, int screenHeight) { + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + + if (this.handle != 0) { + glfwSetWindowSize(this.handle, screenWidth, screenHeight); } + + return this; + } + + public OwoWindow title(String title) { + this.title = title; - this.registration = OpenWindows.add(this); + if (this.handle != 0) { + glfwSetWindowTitle(this.handle, title); + } + + return this; + } + + public OwoWindow icon(WindowIcon icon) { + this.icon = icon; + + if (this.handle != 0) { + applyIcon(); + } + + return this; + } + + public void open() { + try (var ignored = OwoGlUtil.setContext(0)) { + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1); + this.handle = glfwCreateWindow(this.screenWidth, this.screenHeight, this.title, 0, MinecraftClient.getInstance().getWindow().getHandle()); + + if (this.handle == 0) { + throw new IllegalStateException("OwoWindow creation failed due to GLFW error"); + } + + glfwMakeContextCurrent(this.handle); + glfwSwapInterval(0); + } + + applyIcon(); + + int[] framebufferWidthArr = new int[1]; + int[] framebufferHeightArr = new int[1]; + glfwGetFramebufferSize(this.handle, framebufferWidthArr, framebufferHeightArr); + this.framebufferWidth = framebufferWidthArr[0]; + this.framebufferHeight = framebufferHeightArr[0]; + + this.framebuffer = new SimpleFramebuffer(this.framebufferWidth, this.framebufferHeight, true, MinecraftClient.IS_SYSTEM_MAC); + + try (var ignored = OwoGlUtil.setContext(this.handle)) { + GlDebug.enableDebug(client.options.glDebugVerbosity, true); + } + + initLocalFramebuffer(); + + glfwSetWindowCloseCallback(handle, stowAndReturn(GLFWWindowCloseCallback.create(window -> { + try (var ignored = CurrentWindowContext.setCurrent(this)) { + this.close(); + } + }))); + + glfwSetWindowSizeCallback(handle, stowAndReturn(GLFWWindowSizeCallback.create((window, width, height) -> { + this.screenWidth = width; + this.screenHeight = height; + }))); + + glfwSetFramebufferSizeCallback(handle, stowAndReturn(GLFWFramebufferSizeCallback.create((window, width, height) -> { + if (this.framebufferWidth == width && this.framebufferHeight == height) return; + + this.framebufferWidth = width; + this.framebufferHeight = height; + + try (var ignored = OwoGlUtil.setContext(client.getWindow().getHandle())) { + framebuffer.delete(); + + this.framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + } + + initLocalFramebuffer(); - windowClosed().subscribe(this::close); - framebufferResized().subscribe((newWidth, newHeight) -> { recalculateScale(); - adapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); - }); - mouseMoved().subscribe((x, y) -> { - int newX = (int) (x / scaleFactor); - int newY = (int) (y / scaleFactor); + + try (var ignored = CurrentWindowContext.setCurrent(this)) { + uiAdapter.moveAndResize(0, 0, scaledWidth(), scaledHeight()); + + this.framebufferResizedEvents.sink().onFramebufferResized(width, height); + } + }))); + + glfwSetCursorPosCallback(handle, stowAndReturn(GLFWCursorPosCallback.create((window, xpos, ypos) -> { + int newX = (int) (xpos / scaleFactor); + int newY = (int) (ypos / scaleFactor); deltaX += newX - mouseX; deltaY += newY - mouseY; mouseY = newY; mouseX = newX; - }); - mouseButton().subscribe((button, released) -> { - if (released) { - this.activeButton = -1; + }))); + + glfwSetMouseButtonCallback(handle, stowAndReturn(GLFWMouseButtonCallback.create((window, button, action, mods) -> { + try (var ignored = CurrentWindowContext.setCurrent(this)) { + if (action == GLFW_RELEASE) { + this.activeButton = -1; + + uiAdapter.mouseReleased(mouseX, mouseY, button); + } else { + this.activeButton = button; + + uiAdapter.mouseClicked(mouseX, mouseY, button); + } + } + }))); + + glfwSetScrollCallback(handle, stowAndReturn(GLFWScrollCallback.create((window, xoffset, yoffset) -> { + try (var ignored = CurrentWindowContext.setCurrent(this)) { + double yAmount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yoffset) : yoffset) + * client.options.getMouseWheelSensitivity().getValue(); + double xAmount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(xoffset) : xoffset) + * client.options.getMouseWheelSensitivity().getValue(); + uiAdapter.mouseScrolled(mouseX, mouseY, xAmount, yAmount); + } + }))); + + glfwSetKeyCallback(handle, stowAndReturn(GLFWKeyCallback.create((window, key, scancode, action, mods) -> { + try (var ignored = CurrentWindowContext.setCurrent(this)) { + if (action == GLFW_RELEASE) { + uiAdapter.keyReleased(key, scancode, mods); + } else { + uiAdapter.keyPressed(key, scancode, mods); + } + } + }))); + + glfwSetCharModsCallback(handle, stowAndReturn(GLFWCharModsCallback.create((window, codepoint, mods) -> { + try (var ignored = CurrentWindowContext.setCurrent(this)) { + uiAdapter.charTyped((char) codepoint, mods); + } + }))); + + recalculateScale(); + + try (var ignored = CurrentWindowContext.setCurrent(this)) { + this.uiAdapter = createAdapter(); + build(this.uiAdapter.rootComponent); + this.uiAdapter.inflateAndMount(); + } + + OpenWindows.add(this); + } + + private T stowAndReturn(T resource) { + this.disposeList.add(resource); + return resource; + } + + private void applyIcon() { + if (icon == null) return; - adapter.mouseReleased(mouseX, mouseY, button); - } else { - this.activeButton = button; + List icons = icon.listIconImages(); - adapter.mouseClicked(mouseX, mouseY, button); + List freeList = new ArrayList<>(icons.size()); + try (MemoryStack memoryStack = MemoryStack.stackPush()) { + GLFWImage.Buffer buffer = GLFWImage.malloc(icons.size(), memoryStack); + + for (int i = 0; i < icons.size(); i++) { + NativeImage icon = icons.get(i); + ByteBuffer imgBuffer = MemoryUtil.memAlloc(icon.getWidth() * icon.getHeight() * 4); + freeList.add(imgBuffer); + imgBuffer.asIntBuffer().put(icon.copyPixelsRgba()); + + buffer + .position(i) + .width(icon.getWidth()) + .height(icon.getHeight()) + .pixels(imgBuffer); + } + + GLFW.glfwSetWindowIcon(this.handle, buffer.position(0)); + } finally { + freeList.forEach(MemoryUtil::memFree); + + if (icon.closeAfterUse()) + icons.forEach(NativeImage::close); + } + } + + private void initLocalFramebuffer() { + try (var ignored = OwoGlUtil.setContext(this.handle)) { + if (localFramebuffer != 0) { + GL32.glDeleteFramebuffers(localFramebuffer); } - }); - mouseScrolled().subscribe((xOffset, yOffset) -> { - double yAmount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(yOffset) : yOffset) - * client.options.getMouseWheelSensitivity().getValue(); - double xAmount = (client.options.getDiscreteMouseScroll().getValue() ? Math.signum(xOffset) : xOffset) - * client.options.getMouseWheelSensitivity().getValue(); - adapter.mouseScrolled(mouseX, mouseY, xAmount, yAmount); - }); - keyPressed().subscribe((keyCode, scanCode, modifiers, released) -> { - if (released) return; - - adapter.keyPressed(keyCode, scanCode, modifiers); - }); - charTyped().subscribe(adapter::charTyped); + + this.localFramebuffer = GL32.glGenFramebuffers(); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.localFramebuffer); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.framebuffer.getColorAttachment(), 0); + + int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); + if (status != GL32.GL_FRAMEBUFFER_COMPLETE) + throw new IllegalStateException("Failed to create local framebuffer!"); + } } protected abstract OwoUIAdapter createAdapter(); @@ -112,9 +312,9 @@ public void recalculateScale() { private void tickMouse() { if (deltaX == 0 && this.deltaY == 0) return; - adapter.mouseMoved(mouseX, mouseY); + uiAdapter.mouseMoved(mouseX, mouseY); - if (activeButton != -1) adapter.mouseDragged(mouseX, mouseY, activeButton, deltaX, deltaY); + if (activeButton != -1) uiAdapter.mouseDragged(mouseX, mouseY, activeButton, deltaX, deltaY); deltaX = 0; deltaY = 0; @@ -150,17 +350,55 @@ public void render() { DiffuseLighting.enableGuiDepthLighting(); var consumers = client.getBufferBuilders().getEntityVertexConsumers(); - adapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getRenderTickCounter().getTickDelta(false)); + uiAdapter.render(new DrawContext(client, consumers), mouseX, mouseY, client.getRenderTickCounter().getTickDelta(false)); consumers.draw(); RenderSystem.getModelViewStack().popMatrix(); RenderSystem.applyModelViewMatrix(); } - framebuffer().endWrite(); + framebuffer.endWrite(); } - present(); + try (var ignored = OwoGlUtil.setContext(handle)) { + // This code intentionally doesn't use Minecraft's RenderSystem + // class, as it caches GL state that is invalid on this context. + GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, localFramebuffer); + GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, 0); + + GL32.glClearColor(1, 1, 1, 1); + GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); + GL32.glBlitFramebuffer(0, 0, this.framebufferWidth, this.framebufferHeight, 0, 0, this.framebufferWidth, this.framebufferHeight, GL32.GL_COLOR_BUFFER_BIT, GL32.GL_NEAREST); + + // Intentionally doesn't poll events so that all events are on the main window + Tessellator.getInstance().clear(); + GLFW.glfwSwapBuffers(this.handle); + } + } + + @Override + public long handle() { + return handle; + } + + @Override + public EventSource framebufferResized() { + return framebufferResizedEvents.source(); + } + + @Override + public Framebuffer framebuffer() { + return framebuffer; + } + + @Override + public int framebufferWidth() { + return framebufferWidth; + } + + @Override + public int framebufferHeight() { + return framebufferHeight; } @Override @@ -178,10 +416,24 @@ public int scaledHeight() { return scaledHeight; } - @Override + public boolean closed() { + return this.handle == 0; + } + public void close() { - adapter.dispose(); - registration.close(); - super.close(); + this.destroyFeatures(); + uiAdapter.dispose(); + OpenWindows.remove(this); + + try (var ignored = OwoGlUtil.setContext(this.handle)) { + GL32.glDeleteFramebuffers(this.localFramebuffer); + } + + this.framebuffer.delete(); + glfwDestroyWindow(this.handle); + this.handle = 0; + + this.disposeList.forEach(NativeResource::free); + this.disposeList.clear(); } } diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowClosed.java b/src/main/java/io/wispforest/owo/ui/window/WindowClosed.java deleted file mode 100644 index 3266d704..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/WindowClosed.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.wispforest.owo.ui.window; - -import io.wispforest.owo.util.EventStream; - -public interface WindowClosed { - void onWindowClosed(); - - static EventStream newStream() { - return new EventStream<>(subscribers -> () -> { - for (var subscriber : subscribers) { - subscriber.onWindowClosed(); - } - }); - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowIcon.java b/src/main/java/io/wispforest/owo/ui/window/WindowIcon.java new file mode 100644 index 00000000..633d3ac1 --- /dev/null +++ b/src/main/java/io/wispforest/owo/ui/window/WindowIcon.java @@ -0,0 +1,61 @@ +package io.wispforest.owo.ui.window; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.texture.NativeImage; +import net.minecraft.util.Identifier; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public interface WindowIcon { + static WindowIcon fromResources(Identifier... iconIds) { + return fromResources(List.of(iconIds)); + } + + static WindowIcon fromResources(List iconIds) { + return new WindowIcon() { + @Override + public List listIconImages() { + List iconImages = new ArrayList<>(iconIds.size()); + + for (Identifier iconId : iconIds) { + var icon = MinecraftClient.getInstance().getResourceManager().getResource(iconId).orElse(null); + + if (icon == null) continue; + + try { + iconImages.add(NativeImage.read(icon.getInputStream())); + } catch (IOException e) { + throw new RuntimeException("Couldn't open icon " + iconId, e); + } + } + + return iconImages; + } + + @Override + public boolean closeAfterUse() { + return true; + } + }; + } + + static WindowIcon fromNativeImages(List icons, boolean closeAfterUse) { + return new WindowIcon() { + @Override + public List listIconImages() { + return icons; + } + + @Override + public boolean closeAfterUse() { + return closeAfterUse; + } + }; + } + + List listIconImages(); + + boolean closeAfterUse(); +} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java b/src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java deleted file mode 100644 index 4dd64189..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/WindowKeyPressed.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.wispforest.owo.ui.window; - -import io.wispforest.owo.util.EventStream; - -public interface WindowKeyPressed { - void onKeyPressed(int keyCode, int scanCode, int modifiers, boolean released); - - static EventStream newStream() { - return new EventStream<>(subscribers -> (keyCode, scanCode, modifiers, released) -> { - for (var subscriber : subscribers) { - subscriber.onKeyPressed(keyCode, scanCode, modifiers, released); - } - }); - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java b/src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java deleted file mode 100644 index 01ed3832..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/WindowMouseButton.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.wispforest.owo.ui.window; - -import io.wispforest.owo.util.EventStream; - -public interface WindowMouseButton { - void onMouseButton(int button, boolean released); - - static EventStream newStream() { - return new EventStream<>(subscribers -> (button, released) -> { - for (var subscriber : subscribers) { - subscriber.onMouseButton(button, released); - } - }); - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java b/src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java deleted file mode 100644 index c29649d1..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/WindowMouseMoved.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.wispforest.owo.ui.window; - -import io.wispforest.owo.util.EventStream; - -public interface WindowMouseMoved { - void onMouseMoved(double x, double y); - - static EventStream newStream() { - return new EventStream<>(subscribers -> (x, y) -> { - for (var subscriber : subscribers) { - subscriber.onMouseMoved(x, y); - } - }); - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java b/src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java deleted file mode 100644 index 2c9a3033..00000000 --- a/src/main/java/io/wispforest/owo/ui/window/WindowMouseScrolled.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.wispforest.owo.ui.window; - -import io.wispforest.owo.util.EventStream; - -public interface WindowMouseScrolled { - void onMouseScrolled(double xOffset, double yOffset); - - static EventStream newStream() { - return new EventStream<>(subscribers -> (xOffset, yOffset) -> { - for (var subscriber : subscribers) { - subscriber.onMouseScrolled(xOffset, yOffset); - } - }); - } -} diff --git a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java index ea9a25eb..7f0b2c8c 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/VanillaWindowContext.java @@ -6,17 +6,20 @@ import io.wispforest.owo.util.EventStream; import io.wispforest.owo.util.SupportsFeaturesImpl; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.util.Window; public class VanillaWindowContext extends SupportsFeaturesImpl implements WindowContext { - public static final VanillaWindowContext MAIN = new VanillaWindowContext(MinecraftClient.getInstance().getWindow()); + public static final VanillaWindowContext MAIN = new VanillaWindowContext(MinecraftClient.getInstance().getWindow(), MinecraftClient.getInstance().getFramebuffer()); private final Window window; + private final Framebuffer framebuffer; private final EventStream framebufferResizedEvents = WindowFramebufferResized.newStream(); - public VanillaWindowContext(Window window) { + public VanillaWindowContext(Window window, Framebuffer framebuffer) { this.window = window; + this.framebuffer = framebuffer; WindowResizeCallback.EVENT.register((client, window1) -> { if (window != window1) return; @@ -40,6 +43,11 @@ public EventSource framebufferResized() { return framebufferResizedEvents.source(); } + @Override + public Framebuffer framebuffer() { + return framebuffer; + } + @Override public int scaledWidth() { return window.getScaledWidth(); diff --git a/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java b/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java index 6d139f86..f1a104e7 100644 --- a/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java +++ b/src/main/java/io/wispforest/owo/ui/window/context/WindowContext.java @@ -3,11 +3,13 @@ import io.wispforest.owo.ui.window.WindowFramebufferResized; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.SupportsFeatures; +import net.minecraft.client.gl.Framebuffer; public interface WindowContext extends SupportsFeatures { int framebufferWidth(); int framebufferHeight(); EventSource framebufferResized(); + Framebuffer framebuffer(); int scaledWidth(); int scaledHeight(); diff --git a/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java b/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java index 8bd87ab2..fa17b077 100644 --- a/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java +++ b/src/main/java/io/wispforest/owo/util/SupportsFeaturesImpl.java @@ -3,7 +3,7 @@ import java.util.HashMap; import java.util.Map; -public abstract class SupportsFeaturesImpl> implements SupportsFeatures, AutoCloseable { +public abstract class SupportsFeaturesImpl> implements SupportsFeatures { private final Map, Object> features = new HashMap<>(); @SuppressWarnings("unchecked") @@ -19,8 +19,7 @@ public T get(Key key) { return value; } - @Override - public void close() { + public void destroyFeatures() { for (var feature : features.values()) { try { if (feature instanceof AutoCloseable closeable) { diff --git a/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java b/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java index 6e23ad53..df52b8bb 100644 --- a/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java +++ b/src/testmod/java/io/wispforest/uwu/client/ComponentTestScreen.java @@ -59,7 +59,6 @@ protected void init() { .child(Components.button(Text.of("No Background"), button -> rootComponent.surface(Surface.BLANK)).margins(Insets.vertical(5)).horizontalSizing(Sizing.fixed(95))) .child(Components.button(Text.of("Dirt Background"), button -> rootComponent.surface(Surface.OPTIONS_BACKGROUND)).horizontalSizing(Sizing.fixed(95))) .child(Components.checkbox(Text.of("bruh")).onChanged(aBoolean -> this.client.player.sendMessage(Text.of("bruh: " + aBoolean))).margins(Insets.top(5))) - .child(Components.button(Text.of("bro it's a window"), button -> new UwuTestWindow()).margins(Insets.vertical(5)).horizontalSizing(Sizing.fixed(95))) .padding(Insets.of(10)) .surface(Surface.flat(0x77000000)) .positioning(Positioning.relative(1, 1)) diff --git a/src/testmod/java/io/wispforest/uwu/client/SelectUwuScreenScreen.java b/src/testmod/java/io/wispforest/uwu/client/SelectUwuScreenScreen.java index 3860a214..6ca0a46d 100644 --- a/src/testmod/java/io/wispforest/uwu/client/SelectUwuScreenScreen.java +++ b/src/testmod/java/io/wispforest/uwu/client/SelectUwuScreenScreen.java @@ -53,6 +53,7 @@ protected void build(FlowLayout rootComponent) {} }))); panel.child(Components.button(Text.literal("smolnite"), button -> this.client.setScreen(new SmolComponentTestScreen()))); panel.child(Components.button(Text.literal("sizenite"), button -> this.client.setScreen(new SizingTestScreen()))); + panel.child(Components.button(Text.literal("multiwindow demo"), button -> UwuTestWindow.openWindow())); this.uiAdapter.rootComponent.child(panel); } diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index 0d715614..bf400b18 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -6,6 +6,7 @@ import io.wispforest.owo.ui.container.FlowLayout; import io.wispforest.owo.ui.core.*; import io.wispforest.owo.ui.window.OwoWindow; +import io.wispforest.owo.ui.window.WindowIcon; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import net.minecraft.util.Formatting; @@ -14,10 +15,12 @@ import java.util.List; public class UwuTestWindow extends OwoWindow { - public UwuTestWindow() { - super(640, 480, "uωu test window!", MinecraftClient.getInstance().getWindow().getHandle()); - - setIconTextures(MinecraftClient.getInstance().getResourceManager(), List.of(Identifier.of("owo", "icon.png"))); + public static void openWindow() { + new UwuTestWindow() + .size(640, 480) + .title("uωu test window!") + .icon(WindowIcon.fromResources(Identifier.of("owo", "icon.png"))) + .open(); } @Override @@ -41,17 +44,20 @@ protected void build(FlowLayout rootComponent) { textbox.onChanged().subscribe(value -> { if (value.equalsIgnoreCase("yes")) { statusLabel.text(Text.literal("Owl :)") - .formatted(Formatting.GREEN)); + .formatted(Formatting.GREEN)); + this.title("uωu test window! [owl :)]"); } else { statusLabel.text(Text.literal("Not an owl :(") - .formatted(Formatting.RED)); + .formatted(Formatting.RED)); + + this.title("uωu test window! [not owl :(]"); } }); inner - .child(textbox - .margins(Insets.vertical(5))) - .child(statusLabel); + .child(textbox + .margins(Insets.vertical(5))) + .child(statusLabel); inner.child(Containers.horizontalFlow(Sizing.content(), Sizing.content()) .child(Components.label(Text.literal("owl level: "))) From 20fbdf92efbb9f2cb4d68b2f393bf447e5c21944 Mon Sep 17 00:00:00 2001 From: Basique Date: Thu, 4 Jul 2024 01:09:24 +0300 Subject: [PATCH 18/20] add windowHint method also make uwu test window transparent --- .../wispforest/owo/ui/window/OwoWindow.java | 22 ++++++++++++++++--- .../wispforest/uwu/client/UwuTestWindow.java | 7 +++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index 5b6ef74a..d695b607 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -9,7 +9,6 @@ import io.wispforest.owo.ui.window.context.WindowContext; import io.wispforest.owo.util.EventSource; import io.wispforest.owo.util.EventStream; -import io.wispforest.owo.util.InfallibleCloseable; import io.wispforest.owo.util.SupportsFeaturesImpl; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; @@ -19,6 +18,7 @@ import net.minecraft.client.render.DiffuseLighting; import net.minecraft.client.render.Tessellator; import net.minecraft.client.texture.NativeImage; +import net.minecraft.util.Pair; import org.joml.Matrix4f; import org.joml.Matrix4fStack; import org.lwjgl.glfw.*; @@ -38,7 +38,8 @@ public abstract class OwoWindow extends SupportsFeatu private String title = "owo-ui window"; private int screenWidth = 854; private int screenHeight = 480; - private WindowIcon icon; + private WindowIcon icon = null; + private final List> windowHints = new ArrayList<>(); private int framebufferWidth; private int framebufferHeight; @@ -97,6 +98,16 @@ public OwoWindow icon(WindowIcon icon) { return this; } + public OwoWindow windowHint(int hint, int value) { + if (this.handle != 0) { + throw new IllegalStateException("Tried to add window hint after window was opened"); + } + + windowHints.add(new Pair<>(hint, value)); + + return this; + } + public void open() { try (var ignored = OwoGlUtil.setContext(0)) { glfwDefaultWindowHints(); @@ -106,6 +117,11 @@ public void open() { glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1); + + for (var hint : windowHints) { + glfwWindowHint(hint.getLeft(), hint.getRight()); + } + this.handle = glfwCreateWindow(this.screenWidth, this.screenHeight, this.title, 0, MinecraftClient.getInstance().getWindow().getHandle()); if (this.handle == 0) { @@ -328,7 +344,7 @@ public void render() { framebuffer().beginWrite(true); - RenderSystem.clearColor(0, 0, 0, 1); + RenderSystem.clearColor(0, 0, 0, 0); RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); Matrix4f matrix4f = new Matrix4f() diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index bf400b18..79bcd5f5 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -7,12 +7,10 @@ import io.wispforest.owo.ui.core.*; import io.wispforest.owo.ui.window.OwoWindow; import io.wispforest.owo.ui.window.WindowIcon; -import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; - -import java.util.List; +import org.lwjgl.glfw.GLFW; public class UwuTestWindow extends OwoWindow { public static void openWindow() { @@ -20,6 +18,7 @@ public static void openWindow() { .size(640, 480) .title("uωu test window!") .icon(WindowIcon.fromResources(Identifier.of("owo", "icon.png"))) + .windowHint(GLFW.GLFW_TRANSPARENT_FRAMEBUFFER, 1) .open(); } @@ -30,7 +29,7 @@ protected OwoUIAdapter createAdapter() { @Override protected void build(FlowLayout rootComponent) { - rootComponent.surface(Surface.DARK_PANEL); +// rootComponent.surface(Surface.DARK_PANEL); rootComponent.padding(Insets.of(10)); var inner = Containers.verticalFlow(Sizing.content(), Sizing.content()); From 6d2d7df60c0b59b8dc51190275cce835b3a91f47 Mon Sep 17 00:00:00 2001 From: Basique Date: Thu, 4 Jul 2024 21:48:07 +0300 Subject: [PATCH 19/20] cursed garbage to support moving window during drag --- .../wispforest/owo/ui/window/OwoWindow.java | 29 +++++++++-- .../uwu/client/MoveButtonComponent.java | 52 +++++++++++++++++++ .../wispforest/uwu/client/UwuTestWindow.java | 1 + 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/testmod/java/io/wispforest/uwu/client/MoveButtonComponent.java diff --git a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java index d695b607..ed73baea 100644 --- a/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java +++ b/src/main/java/io/wispforest/owo/ui/window/OwoWindow.java @@ -35,6 +35,8 @@ import static org.lwjgl.glfw.GLFW.glfwSetCharModsCallback; public abstract class OwoWindow extends SupportsFeaturesImpl implements WindowContext { + private static final boolean USE_GLOBAL_POS = glfwGetPlatform() != GLFW_PLATFORM_WAYLAND; + private String title = "owo-ui window"; private int screenWidth = 854; private int screenHeight = 480; @@ -57,10 +59,15 @@ public abstract class OwoWindow extends SupportsFeatu private int mouseX = -1; private int mouseY = -1; + private int globalMouseX = -1; + private int globalMouseY = -1; private int deltaX = 0; private int deltaY = 0; private int activeButton = -1; + private final int[] globalX = new int[1]; + private final int[] globalY = new int[1]; + private final EventStream framebufferResizedEvents = WindowFramebufferResized.newStream(); public OwoWindow() { @@ -186,11 +193,25 @@ public void open() { int newX = (int) (xpos / scaleFactor); int newY = (int) (ypos / scaleFactor); - deltaX += newX - mouseX; - deltaY += newY - mouseY; + if (!USE_GLOBAL_POS) { + this.deltaX += newX - mouseX; + this.deltaY += newY - mouseY; + } + + this.mouseY = newY; + this.mouseX = newX; - mouseY = newY; - mouseX = newX; + if (USE_GLOBAL_POS) { + glfwGetWindowPos(handle, this.globalX, this.globalY); + int newGlobalX = (int) ((this.globalX[0] + xpos) / scaleFactor); + int newGlobalY = (int) ((this.globalY[0] + ypos) / scaleFactor); + + this.deltaX += newGlobalX - this.globalMouseX; + this.deltaY += newGlobalY - this.globalMouseY; + + this.globalMouseX = newGlobalX; + this.globalMouseY = newGlobalY; + } }))); glfwSetMouseButtonCallback(handle, stowAndReturn(GLFWMouseButtonCallback.create((window, button, action, mods) -> { diff --git a/src/testmod/java/io/wispforest/uwu/client/MoveButtonComponent.java b/src/testmod/java/io/wispforest/uwu/client/MoveButtonComponent.java new file mode 100644 index 00000000..89cc43bd --- /dev/null +++ b/src/testmod/java/io/wispforest/uwu/client/MoveButtonComponent.java @@ -0,0 +1,52 @@ +package io.wispforest.uwu.client; + +import io.wispforest.owo.ui.component.ButtonComponent; +import io.wispforest.owo.ui.window.OwoWindow; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import net.minecraft.text.Text; +import org.lwjgl.glfw.GLFW; + +import java.util.function.Consumer; + +public class MoveButtonComponent extends ButtonComponent { + private boolean buttoning = false; + + public MoveButtonComponent(Text message, Consumer onPress) { + super(message, onPress); + } + + @Override + public boolean onMouseDown(double mouseX, double mouseY, int button) { + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT) this.buttoning = true; + + return super.onMouseDown(mouseX, mouseY, button); + } + + @Override + public boolean onMouseUp(double mouseX, double mouseY, int button) { + buttoning = false; + + return super.onMouseUp(mouseX, mouseY, button); + } + + @Override + public boolean onMouseDrag(double mouseX, double mouseY, double deltaX, double deltaY, int button) { + if (buttoning) { + var window = (OwoWindow) CurrentWindowContext.current(); + int[] windowWidth = new int[1]; + int[] windowHeight = new int[1]; + + GLFW.glfwGetWindowSize(window.handle(), windowWidth, windowHeight); + + double factor = window.scaleFactor() * windowWidth[0] / window.framebufferWidth(); + + int[] windowX = new int[1]; + int[] windowY = new int[1]; + + GLFW.glfwGetWindowPos(window.handle(), windowX, windowY); + GLFW.glfwSetWindowPos(window.handle(), windowX[0] + (int)(deltaX * factor), windowY[0] + (int)(deltaY * factor)); + } + + return super.onMouseDrag(mouseX, mouseY, deltaX, deltaY, button); + } +} diff --git a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java index 79bcd5f5..8a6ede38 100644 --- a/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java +++ b/src/testmod/java/io/wispforest/uwu/client/UwuTestWindow.java @@ -35,6 +35,7 @@ protected void build(FlowLayout rootComponent) { var inner = Containers.verticalFlow(Sizing.content(), Sizing.content()); rootComponent.child(Containers.verticalScroll(Sizing.fill(100), Sizing.fill(100), inner)); + inner.child(new MoveButtonComponent(Text.literal("hehe move"), unused -> {})); inner.child(Components.label(Text.of("Are you an owl?"))); var textbox = Components.textBox(Sizing.fixed(60)); From 200323721019191337f3d08c7e0e444430d802ad Mon Sep 17 00:00:00 2001 From: Basique Date: Mon, 15 Jul 2024 18:40:13 +0300 Subject: [PATCH 20/20] migrate BlurProgram to WindowContext features --- .../io/wispforest/owo/shader/BlurProgram.java | 38 +++++++++++++------ .../owo/ui/container/RenderEffectWrapper.java | 1 - .../owo/ui/core/OwoUIDrawContext.java | 1 - 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/wispforest/owo/shader/BlurProgram.java b/src/main/java/io/wispforest/owo/shader/BlurProgram.java index f0bc937a..bfbb59c7 100644 --- a/src/main/java/io/wispforest/owo/shader/BlurProgram.java +++ b/src/main/java/io/wispforest/owo/shader/BlurProgram.java @@ -1,7 +1,9 @@ package io.wispforest.owo.shader; import io.wispforest.owo.ui.core.Surface; -import io.wispforest.owo.ui.event.WindowResizeCallback; +import io.wispforest.owo.ui.window.context.CurrentWindowContext; +import io.wispforest.owo.ui.window.context.WindowContext; +import io.wispforest.owo.util.SupportsFeatures; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.GlUniform; @@ -21,15 +23,9 @@ public class BlurProgram extends GlProgram { private GlUniform directions; private GlUniform quality; private GlUniform size; - private Framebuffer input; public BlurProgram() { super(Identifier.of("owo", "blur"), VertexFormats.POSITION); - - WindowResizeCallback.EVENT.register((client, window) -> { - if (this.input == null) return; - this.input.resize(window.getFramebufferWidth(), window.getFramebufferHeight(), MinecraftClient.IS_SYSTEM_MAC); - }); } public void setParameters(int directions, float quality, float size) { @@ -40,15 +36,17 @@ public void setParameters(int directions, float quality, float size) { @Override public void use() { - var buffer = MinecraftClient.getInstance().getFramebuffer(); + var window = CurrentWindowContext.current(); + var buffer = window.framebuffer(); + var input = window.get(BlurInputFeature.KEY).input; - this.input.beginWrite(false); + input.beginWrite(false); GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, buffer.fbo); GL30.glBlitFramebuffer(0, 0, buffer.textureWidth, buffer.textureHeight, 0, 0, buffer.textureWidth, buffer.textureHeight, GL30.GL_COLOR_BUFFER_BIT, GL30.GL_LINEAR); buffer.beginWrite(false); this.inputResolution.set((float) buffer.textureWidth, (float) buffer.textureHeight); - this.backingProgram.addSampler("InputSampler", this.input.getColorAttachment()); + this.backingProgram.addSampler("InputSampler", input.getColorAttachment()); super.use(); } @@ -59,8 +57,24 @@ protected void setup() { this.directions = this.findUniform("Directions"); this.quality = this.findUniform("Quality"); this.size = this.findUniform("Size"); + } + + private static final class BlurInputFeature implements AutoCloseable { + private static final SupportsFeatures.Key KEY = new SupportsFeatures.Key<>(BlurInputFeature::new); + + private final Framebuffer input; + + public BlurInputFeature(WindowContext ctx) { + this.input = new SimpleFramebuffer(ctx.framebufferWidth(), ctx.framebufferHeight(), false, MinecraftClient.IS_SYSTEM_MAC); + + ctx.framebufferResized().subscribe((newWidth, newHeight) -> { + this.input.resize(newWidth, newHeight, MinecraftClient.IS_SYSTEM_MAC); + }); + } - var window = MinecraftClient.getInstance().getWindow(); - this.input = new SimpleFramebuffer(window.getFramebufferWidth(), window.getFramebufferHeight(), false, MinecraftClient.IS_SYSTEM_MAC); + @Override + public void close() { + this.input.delete(); + } } } \ No newline at end of file diff --git a/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java b/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java index 09c0a7f1..81d0f346 100644 --- a/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java +++ b/src/main/java/io/wispforest/owo/ui/container/RenderEffectWrapper.java @@ -6,7 +6,6 @@ import io.wispforest.owo.ui.core.Component; import io.wispforest.owo.ui.core.OwoUIDrawContext; import io.wispforest.owo.ui.core.Sizing; -import io.wispforest.owo.ui.event.WindowResizeCallback; import io.wispforest.owo.ui.util.ScissorStack; import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.ui.window.context.WindowContext; diff --git a/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java b/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java index c9e8bf29..d644b1fe 100644 --- a/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java +++ b/src/main/java/io/wispforest/owo/ui/core/OwoUIDrawContext.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.systems.RenderSystem; import io.wispforest.owo.client.OwoClient; import io.wispforest.owo.mixin.ui.DrawContextInvoker; -import io.wispforest.owo.ui.event.WindowResizeCallback; import io.wispforest.owo.ui.util.NinePatchTexture; import io.wispforest.owo.ui.window.context.CurrentWindowContext; import io.wispforest.owo.ui.window.context.WindowContext;