From 0c37ac6dfd7f9fa096f3a93bb085335cc56faa7b Mon Sep 17 00:00:00 2001 From: SpaiR Date: Sun, 26 Dec 2021 17:27:36 +0200 Subject: [PATCH] [API] Up GLFW impl --- .../src/main/java/imgui/app/Application.java | 15 +-- .../src/main/java/imgui/gl3/ImGuiImplGl3.java | 4 + .../main/java/imgui/glfw/ImGuiImplGlfw.java | 110 ++++++++++++++---- 3 files changed, 102 insertions(+), 27 deletions(-) diff --git a/imgui-app/src/main/java/imgui/app/Application.java b/imgui-app/src/main/java/imgui/app/Application.java index 2b19544..12208ee 100644 --- a/imgui-app/src/main/java/imgui/app/Application.java +++ b/imgui-app/src/main/java/imgui/app/Application.java @@ -4,8 +4,8 @@ package imgui.app; * Application class from which ImGui applications extend. * Serves as an abstraction layer to hide all low-level details about window creation and rendering routine. * - *

Life-cycle

- * The entry point for ImGui applications is the Application class and {@link #launch(Application)} method. + *

Life-cycle

+ *

The entry point for ImGui applications is the Application class and {@link #launch(Application)} method. * It initializes application instance and starts the main application loop. * *

    @@ -25,8 +25,8 @@ package imgui.app; * Instead of creating widgets and adding listeners to them we have an application loop where everything is handled right away. * Read more about Immediate GUI mode to understand that paradigm better. * - *

    Example

    - * The simplest application example could be done in the next way: + *

    Example

    + *

    The simplest application example could be done in the next way: * *

      * import imgui.ImGui;
    @@ -44,10 +44,11 @@ package imgui.app;
      * }
      * 
    * - *

    As its said, {@link #process()} method is meant to be overridden. All your application logic should go there. + *

    + * As its said, {@link #process()} method is meant to be overridden. All your application logic should go there. * - *

    Threading

    - * Unlike other Java applications, ImGui is about "one thread for everything". You still can use multi-threading, but be careful. + *

    Threading

    + *

    Unlike other Java applications, ImGui is about "one thread for everything". You still can use multi-threading, but be careful. * For example, large list of computations could be separated between application ticks. {@link #process()} method is called constantly. * Use that wisely and remember that all GUI should be in the main thread. */ diff --git a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java index 9905ce8..54d4158 100644 --- a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java +++ b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java @@ -111,6 +111,8 @@ public final class ImGuiImplGl3 { * *

    * If the argument is null, then a "#version 130" string will be used by default. + * + * @param glslVersion string with the version of the GLSL */ public void init(final String glslVersion) { readGlVersion(); @@ -131,6 +133,8 @@ public final class ImGuiImplGl3 { /** * Method to render {@link ImDrawData} into current OpenGL context. + * + * @param drawData draw data to render */ public void renderDrawData(final ImDrawData drawData) { if (drawData.getCmdListsCount() <= 0) { diff --git a/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java b/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java index 522e098..47d8fa9 100644 --- a/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java +++ b/imgui-lwjgl3/src/main/java/imgui/glfw/ImGuiImplGlfw.java @@ -22,6 +22,7 @@ import imgui.flag.ImGuiNavInput; import imgui.flag.ImGuiViewportFlags; import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFWCharCallback; +import org.lwjgl.glfw.GLFWCursorEnterCallback; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWKeyCallback; import org.lwjgl.glfw.GLFWMonitorCallback; @@ -29,6 +30,7 @@ import org.lwjgl.glfw.GLFWMouseButtonCallback; import org.lwjgl.glfw.GLFWNativeWin32; import org.lwjgl.glfw.GLFWScrollCallback; import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.glfw.GLFWWindowFocusCallback; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -93,19 +95,27 @@ public class ImGuiImplGlfw { private final float[] monitorContentScaleY = new float[1]; // GLFW callbacks + private GLFWWindowFocusCallback prevUserCallbackWindowFocus = null; private GLFWMouseButtonCallback prevUserCallbackMouseButton = null; private GLFWScrollCallback prevUserCallbackScroll = null; private GLFWKeyCallback prevUserCallbackKey = null; private GLFWCharCallback prevUserCallbackChar = null; private GLFWMonitorCallback prevUserCallbackMonitor = null; + private GLFWCursorEnterCallback prevUserCallbackCursorEnter = null; // Internal data private boolean callbacksInstalled = false; private boolean wantUpdateMonitors = true; private double time = 0.0; + private long mouseWindowPtr; /** * Method to set the {@link GLFWMouseButtonCallback}. + * + * @param windowId pointer to the window + * @param button clicked mouse button + * @param action click action type + * @param mods click modifiers */ public void mouseButtonCallback(final long windowId, final int button, final int action, final int mods) { if (prevUserCallbackMouseButton != null && windowId == windowPtr) { @@ -119,6 +129,10 @@ public class ImGuiImplGlfw { /** * Method to set the {@link GLFWScrollCallback}. + * + * @param windowId pointer to the window + * @param xOffset scroll offset by x-axis + * @param yOffset scroll offset by y-axis */ public void scrollCallback(final long windowId, final double xOffset, final double yOffset) { if (prevUserCallbackScroll != null && windowId == windowPtr) { @@ -132,6 +146,12 @@ public class ImGuiImplGlfw { /** * Method to set the {@link GLFWKeyCallback}. + * + * @param windowId pointer to the window + * @param key pressed key + * @param scancode key scancode + * @param action press action + * @param mods press modifiers */ public void keyCallback(final long windowId, final int key, final int scancode, final int action, final int mods) { if (prevUserCallbackKey != null && windowId == windowPtr) { @@ -156,8 +176,44 @@ public class ImGuiImplGlfw { io.setKeySuper(io.getKeysDown(GLFW_KEY_LEFT_SUPER) || io.getKeysDown(GLFW_KEY_RIGHT_SUPER)); } + /** + * Method to set the {@link GLFWWindowFocusCallback}. + * + * @param windowId pointer to the window + * @param focused is window focused + */ + public void windowFocusCallback(final long windowId, final boolean focused) { + if (prevUserCallbackWindowFocus != null && windowId == windowPtr) { + prevUserCallbackWindowFocus.invoke(windowId, focused); + } + + ImGui.getIO().addFocusEvent(focused); + } + + /** + * Method to set the {@link GLFWCursorEnterCallback}. + * + * @param windowId pointer to the window + * @param entered is cursor entered + */ + public void cursorEnterCallback(final long windowId, final boolean entered) { + if (prevUserCallbackCursorEnter != null && windowId == windowPtr) { + prevUserCallbackCursorEnter.invoke(windowId, entered); + } + + if (entered) { + mouseWindowPtr = windowId; + } + if (!entered && mouseWindowPtr == windowId) { + mouseWindowPtr = 0; + } + } + /** * Method to set the {@link GLFWCharCallback}. + * + * @param windowId pointer to the window + * @param c pressed char */ public void charCallback(final long windowId, final int c) { if (prevUserCallbackChar != null && windowId == windowPtr) { @@ -170,6 +226,9 @@ public class ImGuiImplGlfw { /** * Method to set the {@link GLFWMonitorCallback}. + * + * @param windowId pointer to the window + * @param event monitor event type (ignored) */ public void monitorCallback(final long windowId, final int event) { wantUpdateMonitors = true; @@ -179,6 +238,10 @@ public class ImGuiImplGlfw { * Method to do an initialization of the {@link ImGuiImplGlfw} state. It SHOULD be called before calling the {@link ImGuiImplGlfw#newFrame()} method. *

    * Method takes two arguments, which should be a valid GLFW window pointer and a boolean indicating whether or not to install callbacks. + * + * @param windowId pointer to the window + * @param installCallbacks should window callbacks be installed + * @return true if everything initialized */ public boolean init(final long windowId, final boolean installCallbacks) { this.windowPtr = windowId; @@ -247,6 +310,8 @@ public class ImGuiImplGlfw { if (installCallbacks) { callbacksInstalled = true; + prevUserCallbackWindowFocus = glfwSetWindowFocusCallback(windowId, this::windowFocusCallback); + prevUserCallbackCursorEnter = glfwSetCursorEnterCallback(windowId, this::cursorEnterCallback); prevUserCallbackMouseButton = glfwSetMouseButtonCallback(windowId, this::mouseButtonCallback); prevUserCallbackScroll = glfwSetScrollCallback(windowId, this::scrollCallback); prevUserCallbackKey = glfwSetKeyCallback(windowId, this::keyCallback); @@ -278,11 +343,6 @@ public class ImGuiImplGlfw { */ public void newFrame() { final ImGuiIO io = ImGui.getIO(); - if (!io.getFonts().isBuilt()) { - throw new IllegalStateException( - "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer init() method? e.g. ImGuiImplGl3.init()" - ); - } glfwGetWindowSize(windowPtr, winWidth, winHeight); glfwGetFramebufferSize(windowPtr, fbWidth, fbHeight); @@ -313,6 +373,8 @@ public class ImGuiImplGlfw { shutdownPlatformInterface(); if (callbacksInstalled) { + glfwSetWindowFocusCallback(windowPtr, prevUserCallbackWindowFocus); + glfwSetCursorEnterCallback(windowPtr, prevUserCallbackCursorEnter); glfwSetMouseButtonCallback(windowPtr, prevUserCallbackMouseButton); glfwSetScrollCallback(windowPtr, prevUserCallbackScroll); glfwSetKeyCallback(windowPtr, prevUserCallbackKey); @@ -362,26 +424,34 @@ public class ImGuiImplGlfw { final boolean focused = glfwGetWindowAttrib(windowPtr, GLFW_FOCUSED) != 0; + final long mouseWindowPtr = (this.mouseWindowPtr == windowPtr || focused) ? windowPtr : 0; + + // Update mouse buttons if (focused) { - if (io.getWantSetMousePos()) { - glfwSetCursorPos(windowPtr, mousePosBackup.x - viewport.getPosX(), mousePosBackup.y - viewport.getPosY()); - } else { - glfwGetCursorPos(windowPtr, mouseX, mouseY); - - if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { - // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) - glfwGetWindowPos(windowPtr, windowX, windowY); - io.setMousePos((float) mouseX[0] + windowX[0], (float) mouseY[0] + windowY[0]); - } else { - // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) - io.setMousePos((float) mouseX[0], (float) mouseY[0]); - } - } - for (int i = 0; i < ImGuiMouseButton.COUNT; i++) { io.setMouseDown(i, glfwGetMouseButton(windowPtr, i) != 0); } } + + // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // (When multi-viewports are enabled, all Dear ImGui positions are same as OS positions) + if (io.getWantSetMousePos() && focused) { + glfwSetCursorPos(windowPtr, mousePosBackup.x - viewport.getPosX(), mousePosBackup.y - viewport.getPosY()); + } + + // Set Dear ImGui mouse position from OS position + if (mouseWindowPtr != 0) { + glfwGetCursorPos(mouseWindowPtr, mouseX, mouseY); + + if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + glfwGetWindowPos(windowPtr, windowX, windowY); + io.setMousePos((float) mouseX[0] + windowX[0], (float) mouseY[0] + windowY[0]); + } else { + // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + io.setMousePos((float) mouseX[0], (float) mouseY[0]); + } + } } }